datadog_backup 3.0.0.alpha.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
+ # Dashboards specific overrides for backup and restore.
4
5
  class Dashboards < Core
6
+ def all
7
+ get_all.fetch('dashboards')
8
+ end
5
9
 
6
10
  def api_version
7
11
  'v1'
@@ -11,12 +15,15 @@ module DatadogBackup
11
15
  'dashboard'
12
16
  end
13
17
 
18
+ def id_keyname
19
+ 'id'
20
+ end
21
+
14
22
  def backup
15
23
  LOGGER.info("Starting diffs on #{::DatadogBackup::ThreadPool::TPOOL.max_length} threads")
16
-
17
- futures = all_dashboards.map do |board|
18
- Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, board) do |board|
19
- id = board['id']
24
+ futures = all.map do |dashboard|
25
+ Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, dashboard) do |board|
26
+ id = board[id_keyname]
20
27
  get_and_write_file(id)
21
28
  end
22
29
  end
@@ -27,10 +34,6 @@ module DatadogBackup
27
34
  Concurrent::Promises.zip(*futures).value!
28
35
  end
29
36
 
30
- def all_dashboards
31
- get_all.fetch('dashboards')
32
- end
33
-
34
37
  def initialize(options)
35
38
  super(options)
36
39
  @banlist = %w[modified_at url].freeze
@@ -1,11 +1,10 @@
1
-
1
+ # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
+ # Notify the user if they are using deprecated features.
4
5
  module Deprecations
5
6
  def self.check
6
- if RUBY_VERSION < '2.7'
7
- LOGGER.warn "ruby-#{RUBY_VERSION} is deprecated. Ruby 2.7 or higher will be required to use this gem after datadog_backup@v3"
8
- end
7
+ LOGGER.warn "ruby-#{RUBY_VERSION} is deprecated. Ruby 2.7 or higher will be required to use this gem after datadog_backup@v3" if RUBY_VERSION < '2.7'
9
8
  end
10
9
  end
11
- end
10
+ end
@@ -6,11 +6,10 @@ require 'yaml'
6
6
  require 'deepsort'
7
7
 
8
8
  module DatadogBackup
9
+ ##
10
+ # Meant to be mixed into DatadogBackup::Core
11
+ # Relies on @options[:backup_dir] and @options[:output_format]
9
12
  module LocalFilesystem
10
- ##
11
- # Meant to be mixed into DatadogBackup::Core
12
- # Relies on @options[:backup_dir] and @options[:output_format]
13
-
14
13
  def all_files
15
14
  ::Dir.glob(::File.join(backup_dir, '**', '*')).select { |f| ::File.file?(f) }
16
15
  end
@@ -46,7 +45,7 @@ module DatadogBackup
46
45
  end
47
46
 
48
47
  def file_type(filepath)
49
- ::File.extname(filepath).strip.downcase[1..-1].to_sym
48
+ ::File.extname(filepath).strip.downcase[1..].to_sym
50
49
  end
51
50
 
52
51
  def find_file_by_id(id)
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
+ # Monitor specific overrides for backup and restore.
4
5
  class Monitors < Core
5
- def all_monitors
6
- @all_monitors ||= get_all
6
+ def all
7
+ get_all
7
8
  end
8
9
 
9
10
  def api_version
@@ -15,14 +16,14 @@ module DatadogBackup
15
16
  end
16
17
 
17
18
  def backup
18
- all_monitors.map do |monitor|
19
+ all.map do |monitor|
19
20
  id = monitor['id']
20
21
  write_file(dump(get_by_id(id)), filename(id))
21
22
  end
22
23
  end
23
24
 
24
25
  def get_by_id(id)
25
- monitor = all_monitors.select { |monitor| monitor['id'].to_s == id.to_s }.first
26
+ monitor = all.select { |m| m['id'].to_s == id.to_s }.first
26
27
  monitor.nil? ? {} : except(monitor)
27
28
  end
28
29
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
+ # Describes what the user wants to see done.
4
5
  module Options
5
6
  def action
6
7
  @options[:action]
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatadogBackup
4
+ # Synthetic specific overrides for backup and restore.
5
+ class Synthetics < Core
6
+ def all
7
+ get_all.fetch('tests')
8
+ end
9
+
10
+ def api_version
11
+ 'v1'
12
+ end
13
+
14
+ def api_resource_name(body = nil)
15
+ return 'synthetics/tests' if body.nil?
16
+ return 'synthetics/tests' if body['type'].nil?
17
+ return 'synthetics/tests/browser' if body['type'].to_s == 'browser'
18
+ return 'synthetics/tests/api' if body['type'].to_s == 'api'
19
+
20
+ raise "Unknown type #{body['type']}"
21
+ end
22
+
23
+ def id_keyname
24
+ 'public_id'
25
+ end
26
+
27
+ def backup
28
+ all.map do |synthetic|
29
+ id = synthetic[id_keyname]
30
+ get_and_write_file(id)
31
+ end
32
+ end
33
+
34
+ def get_by_id(id)
35
+ synthetic = all.select { |s| s[id_keyname].to_s == id.to_s }.first
36
+ synthetic.nil? ? {} : except(synthetic)
37
+ end
38
+
39
+ def initialize(options)
40
+ super(options)
41
+ @banlist = %w[creator created_at modified_at monitor_id public_id].freeze
42
+ end
43
+
44
+ def create(body)
45
+ create_api_resource_name = api_resource_name(body)
46
+ headers = {}
47
+ response = api_service.post("/api/#{api_version}/#{create_api_resource_name}", body, headers)
48
+ resbody = body_with_2xx(response)
49
+ LOGGER.warn "Successfully created #{resbody.fetch(id_keyname)} in datadog."
50
+ LOGGER.info 'Invalidating cache'
51
+ @get_all = nil
52
+ resbody
53
+ end
54
+
55
+ def update(id, body)
56
+ update_api_resource_name = api_resource_name(body)
57
+ headers = {}
58
+ response = api_service.put("/api/#{api_version}/#{update_api_resource_name}/#{id}", body, headers)
59
+ resbody = body_with_2xx(response)
60
+ LOGGER.warn "Successfully restored #{id} to datadog."
61
+ LOGGER.info 'Invalidating cache'
62
+ @get_all = nil
63
+ resbody
64
+ end
65
+ end
66
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
+ # Used by CLI and Dashboards to size thread pool according to available CPU cores.
4
5
  module ThreadPool
5
6
  TPOOL = ::Concurrent::ThreadPoolExecutor.new(
6
7
  min_threads: [2, Concurrent.processor_count].max,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
- VERSION = '3.0.0.alpha.1'
4
+ VERSION = '3.1.0'
5
5
  end
@@ -2,21 +2,18 @@
2
2
 
3
3
  require 'concurrent'
4
4
 
5
- require 'pry' #TODO: remove this
6
- require 'pry-byebug' #TODO: remove this
7
-
8
5
  require_relative 'datadog_backup/local_filesystem'
9
6
  require_relative 'datadog_backup/options'
10
7
  require_relative 'datadog_backup/cli'
11
8
  require_relative 'datadog_backup/core'
12
9
  require_relative 'datadog_backup/dashboards'
13
10
  require_relative 'datadog_backup/monitors'
11
+ require_relative 'datadog_backup/synthetics'
14
12
  require_relative 'datadog_backup/thread_pool'
15
13
  require_relative 'datadog_backup/version'
16
14
  require_relative 'datadog_backup/deprecations'
17
15
  DatadogBackup::Deprecations.check
18
16
 
19
-
17
+ # DatadogBackup is a gem for backing up and restoring Datadog monitors and dashboards.
20
18
  module DatadogBackup
21
19
  end
22
-
data/release.config.js CHANGED
@@ -1,4 +1,8 @@
1
1
  module.exports = {
2
+ "branches": [
3
+ '+([0-9])?(.{+([0-9]),x}).x',
4
+ 'main'
5
+ ],
2
6
  "plugins": [
3
7
  "@semantic-release/commit-analyzer",
4
8
  "@semantic-release/release-notes-generator",
@@ -8,12 +12,6 @@ module.exports = {
8
12
  "changelogFile": "CHANGELOG.md"
9
13
  }
10
14
  ],
11
- [
12
- "semantic-release-rubygem",
13
- {
14
- "gemFileDir": "."
15
- }
16
- ],
17
15
  [
18
16
  "@semantic-release/git",
19
17
  {
@@ -47,8 +47,8 @@ describe DatadogBackup::Cli do
47
47
  dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/deleted.json")
48
48
 
49
49
  stubs.get('/api/v1/dashboard') { all_dashboards }
50
- stubs.get('/api/v1/dashboard/stillthere') {[200, {}, {}]}
51
- stubs.get('/api/v1/dashboard/alsostillthere') {[200, {}, {}]}
50
+ stubs.get('/api/v1/dashboard/stillthere') { respond_with200({}) }
51
+ stubs.get('/api/v1/dashboard/alsostillthere') { respond_with200({}) }
52
52
  end
53
53
 
54
54
  it 'deletes the file locally as well' do
@@ -58,66 +58,81 @@ describe DatadogBackup::Cli do
58
58
  end
59
59
  end
60
60
 
61
- describe '#diffs' do
62
- subject { cli.diffs }
63
-
64
- before do
65
- dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/diffs1.json")
66
- dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/diffs2.json")
67
- dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/diffs3.json")
68
- allow(dashboards).to receive(:get_by_id).and_return({ 'text' => 'diff2' })
69
- end
70
-
71
- it {
72
- expect(subject).to include(
73
- " ---\n id: diffs1\n ---\n-text: diff2\n+text: diff\n",
74
- " ---\n id: diffs3\n ---\n-text: diff2\n+text: diff\n",
75
- " ---\n id: diffs2\n ---\n-text: diff2\n+text: diff\n"
76
- )
77
- }
78
- end
79
-
80
61
  describe '#restore' do
81
- subject { cli.restore }
62
+ subject(:restore) { cli.restore }
82
63
 
83
64
  before do
84
65
  dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/diffs1.json")
85
66
  allow(dashboards).to receive(:get_by_id).and_return({ 'text' => 'diff2' })
67
+ allow(dashboards).to receive(:write_file)
68
+ allow(dashboards).to receive(:update)
86
69
  end
87
70
 
88
71
  example 'starts interactive restore' do
89
72
  allow($stdin).to receive(:gets).and_return('q')
90
73
 
91
- expect { subject }.to(
74
+ expect { restore }.to(
92
75
  output(/\(r\)estore to Datadog, overwrite local changes and \(d\)ownload, \(s\)kip, or \(q\)uit\?/).to_stdout
93
76
  .and(raise_error(SystemExit))
94
77
  )
95
78
  end
96
79
 
97
- example 'restore' do
98
- allow($stdin).to receive(:gets).and_return('r')
99
- expect(dashboards).to receive(:update).with('diffs1', { 'text' => 'diff' })
100
- subject
80
+ context 'when the user chooses to restore' do
81
+ before do
82
+ allow($stdin).to receive(:gets).and_return('r')
83
+ end
84
+
85
+ example 'it restores from disk to server' do
86
+ restore
87
+ expect(dashboards).to have_received(:update).with('diffs1', { 'text' => 'diff' })
88
+ end
101
89
  end
102
90
 
103
- example 'download' do
104
- allow($stdin).to receive(:gets).and_return('d')
105
- expect(dashboards).to receive(:write_file).with(%({\n "text": "diff2"\n}), "#{tempdir}/dashboards/diffs1.json")
106
- subject
91
+ context 'when the user chooses to download' do
92
+ before do
93
+ allow($stdin).to receive(:gets).and_return('d')
94
+ end
95
+
96
+ example 'it writes from server to disk' do
97
+ restore
98
+ expect(dashboards).to have_received(:write_file).with(%({\n "text": "diff2"\n}), "#{tempdir}/dashboards/diffs1.json")
99
+ end
107
100
  end
108
101
 
109
- example 'skip' do
110
- allow($stdin).to receive(:gets).and_return('s')
111
- expect(dashboards).not_to receive(:write_file)
112
- expect(dashboards).not_to receive(:update)
113
- subject
102
+ context 'when the user chooses to skip' do
103
+ before do
104
+ allow($stdin).to receive(:gets).and_return('s')
105
+ end
106
+
107
+ example 'it does not write to disk' do
108
+ restore
109
+ expect(dashboards).not_to have_received(:write_file)
110
+ end
111
+
112
+ example 'it does not update the server' do
113
+ restore
114
+ expect(dashboards).not_to have_received(:update)
115
+ end
114
116
  end
115
117
 
116
- example 'quit' do
117
- allow($stdin).to receive(:gets).and_return('q')
118
- expect(dashboards).not_to receive(:write_file)
119
- expect(dashboards).not_to receive(:update)
120
- expect { subject }.to raise_error(SystemExit)
118
+ context 'when the user chooses to quit' do
119
+ before do
120
+ allow($stdin).to receive(:gets).and_return('q')
121
+ end
122
+
123
+ example 'it exits' do
124
+ expect { restore }.to raise_error(SystemExit)
125
+ end
126
+
127
+ example 'it does not write to disk' do
128
+ restore
129
+ expect(dashboards).not_to have_received(:write_file)
130
+ end
131
+
132
+ example 'it does not update the server' do
133
+ restore
134
+ expect(dashboards).not_to have_received(:update)
135
+ end
121
136
  end
122
137
  end
123
138
  end
@@ -18,11 +18,8 @@ describe DatadogBackup::Core do
18
18
  return core
19
19
  end
20
20
 
21
-
22
-
23
-
24
21
  describe '#diff' do
25
- subject { core.diff('diff') }
22
+ subject(:diff) { core.diff('diff') }
26
23
 
27
24
  before do
28
25
  allow(core).to receive(:get_by_id).and_return({ 'text' => 'diff1', 'extra' => 'diff1' })
@@ -30,13 +27,14 @@ describe DatadogBackup::Core do
30
27
  end
31
28
 
32
29
  it {
33
- expect(subject).to eq <<~EOF
30
+ expect(diff).to eq(<<~EODIFF
34
31
  ---
35
32
  -extra: diff1
36
33
  -text: diff1
37
34
  +extra: diff2
38
35
  +text: diff2
39
- EOF
36
+ EODIFF
37
+ .chomp)
40
38
  }
41
39
  end
42
40
 
@@ -47,11 +45,13 @@ describe DatadogBackup::Core do
47
45
  end
48
46
 
49
47
  describe '#initialize' do
50
- subject { core }
48
+ subject(:mycore) { core }
51
49
 
52
50
  it 'makes the subdirectories' do
53
- expect(FileUtils).to receive(:mkdir_p).with("#{tempdir}/core")
54
- subject
51
+ fileutils = class_double(FileUtils).as_stubbed_const
52
+ allow(fileutils).to receive(:mkdir_p)
53
+ mycore
54
+ expect(fileutils).to have_received(:mkdir_p).with("#{tempdir}/core")
55
55
  end
56
56
  end
57
57
 
@@ -62,25 +62,25 @@ describe DatadogBackup::Core do
62
62
  end
63
63
 
64
64
  describe '#create' do
65
- subject { core.create({ 'a' => 'b' }) }
65
+ subject(:create) { core.create({ 'a' => 'b' }) }
66
66
 
67
67
  example 'it will post /api/v1/dashboard' do
68
68
  allow(core).to receive(:api_version).and_return('v1')
69
69
  allow(core).to receive(:api_resource_name).and_return('dashboard')
70
- stubs.post('/api/v1/dashboard', {'a' => 'b'}) {[200, {}, {'id' => 'whatever-id-abc' }]}
71
- subject
70
+ stubs.post('/api/v1/dashboard', { 'a' => 'b' }) { respond_with200({ 'id' => 'whatever-id-abc' }) }
71
+ create
72
72
  stubs.verify_stubbed_calls
73
73
  end
74
74
  end
75
75
 
76
76
  describe '#update' do
77
- subject { core.update('abc-123-def', { 'a' => 'b' }) }
77
+ subject(:update) { core.update('abc-123-def', { 'a' => 'b' }) }
78
78
 
79
79
  example 'it puts /api/v1/dashboard' do
80
80
  allow(core).to receive(:api_version).and_return('v1')
81
81
  allow(core).to receive(:api_resource_name).and_return('dashboard')
82
- stubs.put('/api/v1/dashboard/abc-123-def', {'a' => 'b'}) {[200, {}, {'id' => 'whatever-id-abc' }]}
83
- subject
82
+ stubs.put('/api/v1/dashboard/abc-123-def', { 'a' => 'b' }) { respond_with200({ 'id' => 'whatever-id-abc' }) }
83
+ update
84
84
  stubs.verify_stubbed_calls
85
85
  end
86
86
 
@@ -88,10 +88,11 @@ describe DatadogBackup::Core do
88
88
  before do
89
89
  allow(core).to receive(:api_version).and_return('v1')
90
90
  allow(core).to receive(:api_resource_name).and_return('dashboard')
91
- stubs.put('/api/v1/dashboard/abc-123-def', {'a' => 'b'}) {[404, {}, {'id' => 'whatever-id-abc' }]}
91
+ stubs.put('/api/v1/dashboard/abc-123-def', { 'a' => 'b' }) { [404, {}, { 'id' => 'whatever-id-abc' }] }
92
92
  end
93
+
93
94
  it 'raises an error' do
94
- expect { subject }.to raise_error(RuntimeError, 'update failed with error 404')
95
+ expect { update }.to raise_error(RuntimeError, 'update failed with error 404')
95
96
  end
96
97
  end
97
98
  end
@@ -100,35 +101,55 @@ describe DatadogBackup::Core do
100
101
  before do
101
102
  allow(core).to receive(:api_version).and_return('api-version-string')
102
103
  allow(core).to receive(:api_resource_name).and_return('api-resource-name-string')
103
- stubs.get('/api/api-version-string/api-resource-name-string/abc-123-def') {[200, {}, {'test' => 'ok' }]}
104
- stubs.get('/api/api-version-string/api-resource-name-string/bad-123-id') {[404, {}, {'error' => 'blahblah_not_found' }]}
104
+ stubs.get('/api/api-version-string/api-resource-name-string/abc-123-def') { respond_with200({ 'test' => 'ok' }) }
105
+ stubs.get('/api/api-version-string/api-resource-name-string/bad-123-id') do
106
+ [404, {}, { 'error' => 'blahblah_not_found' }]
107
+ end
105
108
  allow(core).to receive(:load_from_file_by_id).and_return({ 'load' => 'ok' })
106
109
  end
107
110
 
108
111
  context 'when id exists' do
109
- subject { core.restore('abc-123-def') }
112
+ subject(:restore) { core.restore('abc-123-def') }
110
113
 
111
114
  example 'it calls out to update' do
112
- expect(core).to receive(:update).with('abc-123-def', { 'load' => 'ok' })
113
- subject
115
+ allow(core).to receive(:update)
116
+ restore
117
+ expect(core).to have_received(:update).with('abc-123-def', { 'load' => 'ok' })
114
118
  end
115
119
  end
116
120
 
117
121
  context 'when id does not exist on remote' do
118
- subject { core.restore('bad-123-id') }
122
+ subject(:restore_newly) { core.restore('bad-123-id') }
123
+
124
+ let(:fileutils) { class_double(FileUtils).as_stubbed_const }
119
125
 
120
126
  before do
121
127
  allow(core).to receive(:load_from_file_by_id).and_return({ 'load' => 'ok' })
122
- stubs.put('/api/api-version-string/api-resource-name-string/bad-123-id') {[404, {}, {'error' => 'id not found' }]}
123
- stubs.post('/api/api-version-string/api-resource-name-string', {'load' => 'ok'}) {[200, {}, {'id' => 'my-new-id' }]}
128
+ stubs.put('/api/api-version-string/api-resource-name-string/bad-123-id') do
129
+ [404, {}, { 'error' => 'id not found' }]
130
+ end
131
+ stubs.post('/api/api-version-string/api-resource-name-string', { 'load' => 'ok' }) do
132
+ respond_with200({ 'id' => 'my-new-id' })
133
+ end
134
+ allow(fileutils).to receive(:rm)
135
+ allow(core).to receive(:create).with({ 'load' => 'ok' }).and_return({ 'id' => 'my-new-id' })
136
+ allow(core).to receive(:get_and_write_file)
137
+ allow(core).to receive(:find_file_by_id).with('bad-123-id').and_return('/path/to/bad-123-id.json')
124
138
  end
125
139
 
126
- example 'it calls out to create then saves the new file and deletes the new file' do
127
- expect(core).to receive(:create).with({ 'load' => 'ok' }).and_return({ 'id' => 'my-new-id' })
128
- expect(core).to receive(:get_and_write_file).with('my-new-id')
129
- allow(core).to receive(:find_file_by_id).with('bad-123-id').and_return('/path/to/bad-123-id.json')
130
- expect(FileUtils).to receive(:rm).with('/path/to/bad-123-id.json')
131
- subject
140
+ example 'it calls out to create' do
141
+ restore_newly
142
+ expect(core).to have_received(:create).with({ 'load' => 'ok' })
143
+ end
144
+
145
+ example 'it saves the new file' do
146
+ restore_newly
147
+ expect(core).to have_received(:get_and_write_file).with('my-new-id')
148
+ end
149
+
150
+ example 'it deletes the old file' do
151
+ restore_newly
152
+ expect(fileutils).to have_received(:rm).with('/path/to/bad-123-id.json')
132
153
  end
133
154
  end
134
155
  end
@@ -23,24 +23,6 @@ describe DatadogBackup::Dashboards do
23
23
  'title' => 'foo'
24
24
  }
25
25
  end
26
- let(:all_dashboards) do
27
- [
28
- 200,
29
- {},
30
- {
31
- 'dashboards' => [
32
- dashboard_description
33
- ]
34
- }
35
- ]
36
- end
37
- let(:example_dashboard) do
38
- [
39
- 200,
40
- {},
41
- board_abc_123_def
42
- ]
43
- end
44
26
  let(:board_abc_123_def) do
45
27
  {
46
28
  'graphs' => [
@@ -61,6 +43,8 @@ describe DatadogBackup::Dashboards do
61
43
  'title' => 'example dashboard'
62
44
  }
63
45
  end
46
+ let(:all_dashboards) { respond_with200({ 'dashboards' => [dashboard_description] }) }
47
+ let(:example_dashboard) { respond_with200(board_abc_123_def) }
64
48
 
65
49
  before do
66
50
  stubs.get('/api/v1/dashboard') { all_dashboards }
@@ -71,25 +55,32 @@ describe DatadogBackup::Dashboards do
71
55
  subject { dashboards.backup }
72
56
 
73
57
  it 'is expected to create a file' do
74
- file = double('file')
58
+ file = instance_double(File)
75
59
  allow(File).to receive(:open).with(dashboards.filename('abc-123-def'), 'w').and_return(file)
76
- expect(file).to receive(:write).with(::JSON.pretty_generate(board_abc_123_def.deep_sort))
60
+ allow(file).to receive(:write)
77
61
  allow(file).to receive(:close)
78
62
 
79
63
  dashboards.backup
64
+ expect(file).to have_received(:write).with(::JSON.pretty_generate(board_abc_123_def.deep_sort))
80
65
  end
81
66
  end
82
67
 
83
- describe '#all_dashboards' do
84
- subject { dashboards.all_dashboards }
68
+ describe '#filename' do
69
+ subject { dashboards.filename('abc-123-def') }
70
+
71
+ it { is_expected.to eq("#{tempdir}/dashboards/abc-123-def.json") }
72
+ end
73
+
74
+ describe '#get_by_id' do
75
+ subject { dashboards.get_by_id('abc-123-def') }
85
76
 
86
- it { is_expected.to eq [dashboard_description] }
77
+ it { is_expected.to eq board_abc_123_def }
87
78
  end
88
79
 
89
80
  describe '#diff' do
90
81
  it 'calls the api only once' do
91
82
  dashboards.write_file('{"a":"b"}', dashboards.filename('abc-123-def'))
92
- expect(dashboards.diff('abc-123-def')).to eq(<<~EOF
83
+ expect(dashboards.diff('abc-123-def')).to eq(<<~EODASH
93
84
  ---
94
85
  -description: example dashboard
95
86
  -graphs:
@@ -101,8 +92,8 @@ describe DatadogBackup::Dashboards do
101
92
  - title: example graph
102
93
  -title: example dashboard
103
94
  +a: b
104
- EOF
105
- )
95
+ EODASH
96
+ .chomp)
106
97
  end
107
98
  end
108
99
 
@@ -111,10 +102,4 @@ describe DatadogBackup::Dashboards do
111
102
 
112
103
  it { is_expected.to eq({ a: :b }) }
113
104
  end
114
-
115
- describe '#get_by_id' do
116
- subject { dashboards.get_by_id('abc-123-def') }
117
-
118
- it { is_expected.to eq board_abc_123_def }
119
- end
120
105
  end