status_page-api 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e2639d460eefcc7f5c041c85269d016bcfd49f6e
4
+ data.tar.gz: fbc1dd24fd91594a256db4c0362d3c0dd5fb5020
5
+ SHA512:
6
+ metadata.gz: 5bd5f444101565a3f60c1288a87041564821413a9578a611cf0fef6b9479ba9756de026f794cbfad617774bd8653ce73c94e377e07d99804bf718fb68b02944c
7
+ data.tar.gz: d12f087c994e88c0eac00610e0de4f5b9b6acc53abefc302777d1b0797213ca78b6f58f4965f9323e40b287ee878a61183163e9f7616b2e029b8d7e24b22e2d2
@@ -0,0 +1,3 @@
1
+ .git/
2
+ coverage/
3
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.4.0
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.10
4
+ - 2.2.6
5
+ - 2.3.3
6
+ - 2.4.0
7
+ script:
8
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,74 @@
1
+ # StatusPage::API
2
+
3
+ [![NYU](https://github.com/NYULibraries/nyulibraries-assets/blob/master/lib/assets/images/nyu.png)](https://dev.library.nyu.edu)
4
+ [![Build Status](https://travis-ci.org/NYULibraries/status_page-api.svg)](https://travis-ci.org/NYULibraries/status_page-api)
5
+ [![Code Climate](https://codeclimate.com/github/NYULibraries/status_page-api/badges/gpa.svg)](https://codeclimate.com/github/NYULibraries/status_page-api)
6
+ [![Coverage Status](https://coveralls.io/repos/github/NYULibraries/status_page-api/badge.svg?branch=development)](https://coveralls.io/github/NYULibraries/status_page-api?branch=development)
7
+
8
+ Ruby client for [StatusPage](https://www.statuspage.io/) [REST API](https://doers.statuspage.io/api/v1/).
9
+
10
+ ## Installation
11
+
12
+ ```
13
+ gem 'status_page-api', github: "NYULibraries/status_page-api"
14
+ require 'status_page/api'
15
+ ```
16
+
17
+ You must set `STATUS_PAGE_API_KEY` environment variable to your API key.
18
+
19
+ ## Usage
20
+
21
+ ### Get component list
22
+
23
+ To fetch a list of components for a particular page:
24
+
25
+ ```
26
+ component_list = StatusPage::API::ComponentList.new('page_id')
27
+ component_list.get
28
+ ```
29
+
30
+ You can then interact with the instance as if it were an array:
31
+
32
+ ```
33
+ component_list[0]
34
+ component_list.map(&:id)
35
+ component_list.each do |component|
36
+ # do something with component
37
+ end
38
+ ```
39
+
40
+ ### Get component data
41
+
42
+ To fetch a particular component:
43
+
44
+ ```
45
+ component = StatusPage::API::Component.new('component_id', 'page_id')
46
+ component.get
47
+ ```
48
+
49
+ All data returned by the API exists in instance methods, e.g.:
50
+
51
+ ```
52
+ component.id
53
+ component.name
54
+ component.description
55
+ component.status
56
+ component.page_id
57
+ component.group_id
58
+ component.created_at
59
+ component.updated_at
60
+ ```
61
+
62
+ ### Modifying a component
63
+
64
+ The API only allows the following fields to be modified: `status`, `name`, and `description`.
65
+
66
+ To modify a field:
67
+
68
+ ```
69
+ component.status = 'major_outage'
70
+ component.description = 'Hello world'
71
+ component.save
72
+ ```
73
+
74
+ The `save` method does not catch any errors raised by `RestClient`.
@@ -0,0 +1,4 @@
1
+ require 'status_page/api'
2
+
3
+ module StatusPage
4
+ end
@@ -0,0 +1,11 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'status_page/api/base'
4
+ require 'status_page/api/component_list'
5
+ require 'status_page/api/component'
6
+ require 'status_page/api/exception'
7
+
8
+ module StatusPage
9
+ module API
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ module StatusPage
2
+ module API
3
+ class Base
4
+ # sends request to status page API for given path and method, passing any other options through to RestClient
5
+ # parses the response and returns as a ruby hash or array
6
+ def execute(path, method:, **options)
7
+ default_options = {headers: headers, method: method, url: get_full_url(path)}
8
+ JSON.parse(RestClient::Request.execute(default_options.merge(options)))
9
+ rescue ::RestClient::Exception => e
10
+ raise Exception.new(e)
11
+ end
12
+
13
+ # sends GET request for subclass's resource_path and returns json result as hash
14
+ def get_resource
15
+ execute(resource_path, method: :get)
16
+ end
17
+
18
+ # sends PATCH request for subclass's resource_path and returns json result as hash;
19
+ # accepts a payload parameter to convert to JSON body
20
+ def patch_resource(payload)
21
+ execute(resource_path, method: :patch, payload: payload.to_json)
22
+ end
23
+
24
+ private
25
+
26
+ def resource_path
27
+ raise("Must define resource_path in your subclass")
28
+ end
29
+
30
+ def get_full_url(subpath)
31
+ "https://api.statuspage.io/v1/#{subpath}"
32
+ end
33
+
34
+ def headers
35
+ {"Authorization" => "OAuth #{api_key}", "Content-Type" => "application/json"}
36
+ end
37
+
38
+ def api_key
39
+ ENV['STATUS_PAGE_API_KEY'] || raise("Must specify STATUS_PAGE_API_KEY to use StatusPage")
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ module StatusPage
2
+ module API
3
+ class Component < Base
4
+
5
+ MUTABLE_ATTRIBUTES = %i[name description status]
6
+ IMMUTABLE_ATTRIBUTES = %i[id page_id group_id created_at updated_at position]
7
+
8
+ attr_accessor(*MUTABLE_ATTRIBUTES)
9
+ attr_reader(*IMMUTABLE_ATTRIBUTES)
10
+
11
+ STATUSES = %w[operational degraded_performance partial_outage major_outage]
12
+ SUCCESS_STATUS = 'operational'
13
+
14
+ def initialize(id, page_id)
15
+ assign_attributes(id: id, page_id: page_id)
16
+ end
17
+
18
+ def get
19
+ assign_attributes(get_resource)
20
+ end
21
+
22
+ def save
23
+ assign_attributes(patch_resource({component: mutable_attributes})) if mutable_attributes.any?
24
+ end
25
+
26
+ def failing?
27
+ status != SUCCESS_STATUS
28
+ end
29
+
30
+ def status=(status_type)
31
+ validate_status(status_type)
32
+ @status = status_type
33
+ end
34
+
35
+ def resource_path
36
+ "pages/#{page_id}/components/#{id}.json"
37
+ end
38
+
39
+ def assign_attributes(attributes)
40
+ attributes.each do |key, value|
41
+ instance_variable_set("@#{key}", value)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def mutable_attributes
48
+ MUTABLE_ATTRIBUTES.inject({}) do |result, attr_name|
49
+ result[attr_name] = send(attr_name) if send(attr_name)
50
+ result
51
+ end
52
+ end
53
+
54
+ def validate_status(status_type)
55
+ STATUSES.include?(status_type.to_s) || raise("Status '#{status_type}' not recognized. Valid statuses: #{STATUSES}")
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ module StatusPage
2
+ module API
3
+ class ComponentList < Base
4
+ extend Forwardable
5
+ delegate [:to_a, :to_ary, :[], :map, :each] => :@components
6
+
7
+ attr_accessor :page_id
8
+
9
+ def initialize(page_id)
10
+ @page_id = page_id
11
+ end
12
+
13
+ def get
14
+ @components = get_resource.map do |attributes|
15
+ comp = Component.new attributes["id"], attributes["page_id"]
16
+ comp.assign_attributes attributes
17
+ comp
18
+ end
19
+ end
20
+
21
+ def resource_path
22
+ "pages/#{page_id}/components.json"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module StatusPage
2
+ module API
3
+ class Exception < StandardError
4
+ attr_reader :rest_client_error
5
+
6
+ def initialize(rest_client_error)
7
+ @rest_client_error = rest_client_error
8
+ super(message)
9
+ end
10
+
11
+ # generates message from original error and JSON response
12
+ def message
13
+ "#{rest_client_error.message} (#{response})"
14
+ end
15
+
16
+ # parses error from JSON response if possible
17
+ # if invalid JSON, or JSON missing "error" key, returns full JSON string
18
+ def response
19
+ return "NO RESPONSE" unless rest_client_error.response
20
+ error_text = JSON.parse(rest_client_error.response)["error"]
21
+ error_text.is_a?(Array) ? error_text.join(", ") : error_text
22
+ rescue JSON::ParserError
23
+ rest_client_error.response
24
+ rescue NoMethodError
25
+ rest_client_error.response
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module StatusPage
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+ require 'status_page'
3
+
4
+ describe StatusPage::API::Base do
5
+ let(:base){ described_class.new }
6
+
7
+ describe "execute" do
8
+ context "with STATUS_PAGE_API_KEY" do
9
+ # stub out api key env variable
10
+ let(:status_page_api_key){ "XXXX" }
11
+ around do |example|
12
+ with_modified_env STATUS_PAGE_API_KEY: status_page_api_key do
13
+ example.run
14
+ end
15
+ end
16
+
17
+ context "given successful response" do
18
+ # stub out request/response
19
+ let(:response){ {"name"=>"apple", "id"=>"xyz"} }
20
+ let(:json_response){ response.to_json }
21
+ let(:url_result){ "https://api.statuspage.io/v1/sample/path.json" }
22
+ before do
23
+ allow(RestClient::Request).to receive(:execute).and_return json_response
24
+ end
25
+
26
+ it "should call execute with URL generated from path" do
27
+ expect(RestClient::Request).to receive(:execute).with(hash_including(url: url_result))
28
+ base.execute "sample/path.json", method: :get
29
+ end
30
+
31
+ it "should call execute with method specified" do
32
+ expect(RestClient::Request).to receive(:execute).with(hash_including(method: :patch))
33
+ base.execute "sample/path.json", method: :patch
34
+ end
35
+
36
+ it "should call execute with headers" do
37
+ expect(RestClient::Request).to receive(:execute).with(hash_including(headers: {"Authorization" => "OAuth #{status_page_api_key}", "Content-Type" => "application/json"}))
38
+ base.execute "sample/path.json", method: :get
39
+ end
40
+
41
+ it "should raise error if no method specified" do
42
+ expect{ base.execute "sample/path.json" }.to raise_error ArgumentError
43
+ end
44
+
45
+ it "should call execute with any additional options specified" do
46
+ expect(RestClient::Request).to receive(:execute).with(hash_including(option: :value, arr: [1,2]))
47
+ base.execute "sample/path.json", method: :patch, option: :value, arr: [1,2]
48
+ end
49
+
50
+ it "should return parsed json" do
51
+ expect(JSON).to receive(:parse).with(json_response).and_call_original
52
+ expect(base.execute("sample/path.json", method: :patch)).to eq response
53
+ end
54
+ end
55
+
56
+ context "given response known to RestClient (422)" do
57
+ # stub out request/response
58
+ let(:error){ RestClient::UnprocessableEntity.new }
59
+ let(:json_response){ {"error" => ["Something went wrong"]}.to_json }
60
+ before do
61
+ allow(RestClient::Request).to receive(:execute).and_raise error
62
+ allow(error).to receive(:response).and_return json_response
63
+ end
64
+
65
+ it "should raise validation error parsed from response" do
66
+ expect{ base.execute "sample/path.json", method: :patch }.to raise_error StatusPage::API::Exception, "Unprocessable Entity (Something went wrong)"
67
+ end
68
+ end
69
+
70
+ context "given response unknown to RestClient (420)" do
71
+ # stub out request/response
72
+ let(:error){ RestClient::RequestFailed.new }
73
+ let(:json_response){ {"error" => "Slow your roll"}.to_json }
74
+ let(:http_code){ 420 }
75
+ before do
76
+ allow(RestClient::Request).to receive(:execute).and_raise error
77
+ allow(error).to receive(:response).and_return json_response
78
+ allow(error).to receive(:http_code).and_return http_code
79
+ end
80
+
81
+ it "should raise validation error parsed from response" do
82
+ expect{ base.execute "sample/path.json", method: :patch }.to raise_error StatusPage::API::Exception, "HTTP status code 420 (Slow your roll)"
83
+ end
84
+ end
85
+
86
+ context "given other error response" do
87
+ # stub out request/response
88
+ let(:error){ RestClient::ResourceNotFound.new }
89
+ before do
90
+ allow(RestClient::Request).to receive(:execute).and_raise error
91
+ end
92
+
93
+ it "should raise the error" do
94
+ expect{ base.execute "sample/path.json", method: :patch }.to raise_error StatusPage::API::Exception, "Not Found (NO RESPONSE)"
95
+ end
96
+ end
97
+ end
98
+
99
+ context "without STATUS_PAGE_API_KEY" do
100
+ # stub out api key env variable
101
+ around do |example|
102
+ with_modified_env STATUS_PAGE_API_KEY: nil do
103
+ example.run
104
+ end
105
+ end
106
+
107
+ it "should raise an error without calling api" do
108
+ expect(RestClient::Request).to_not receive(:execute)
109
+ expect{ base.execute "sample/path.json", method: :get }.to raise_error "Must specify STATUS_PAGE_API_KEY to use StatusPage"
110
+ end
111
+ end
112
+
113
+ end
114
+
115
+ describe "get_resource" do
116
+ subject { base.get_resource }
117
+ let(:result){ {a: :b} }
118
+ before { allow(base).to receive(:execute).and_return result }
119
+
120
+ context "with resource_path defined" do
121
+ let(:path){ "/some/path.json" }
122
+ before do
123
+ base.define_singleton_method(:resource_path){ "/some/path.json" }
124
+ end
125
+
126
+ it { is_expected.to eq result }
127
+
128
+ it "should call execute with correct parameters" do
129
+ expect(base).to receive(:execute).with(path, method: :get)
130
+ subject
131
+ end
132
+ end
133
+
134
+ context "without resource_path defined" do
135
+ it "should raise error" do
136
+ expect{ subject }.to raise_error("Must define resource_path in your subclass")
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "patch_resource" do
142
+ subject { base.patch_resource payload }
143
+ let(:payload){ {some: :thing} }
144
+ let(:result){ {a: :b} }
145
+ before { allow(base).to receive(:execute).and_return result }
146
+
147
+ context "with resource_path defined" do
148
+ let(:path){ "/some/path.json" }
149
+ before do
150
+ base.define_singleton_method(:resource_path){ "/some/path.json" }
151
+ end
152
+
153
+ context "with hash payload" do
154
+ it { is_expected.to eq result }
155
+
156
+ it "should call execute with path and method" do
157
+ expect(base).to receive(:execute).with(path, hash_including(method: :patch))
158
+ subject
159
+ end
160
+
161
+ it "should call execute with payload as json" do
162
+ expect(base).to receive(:execute).with(path, hash_including(payload: payload.to_json))
163
+ subject
164
+ end
165
+ end
166
+
167
+ context "with string payload" do
168
+ let(:payload){ "abcd" }
169
+
170
+ it { is_expected.to eq result }
171
+
172
+ it "should call execute with path and method" do
173
+ expect(base).to receive(:execute).with(path, hash_including(method: :patch))
174
+ subject
175
+ end
176
+
177
+ it "should call execute with payload as json" do
178
+ expect(base).to receive(:execute).with(path, hash_including(payload: payload.to_json))
179
+ subject
180
+ end
181
+ end
182
+
183
+ context "with nil payload" do
184
+ let(:payload){ nil }
185
+
186
+ it { is_expected.to eq result }
187
+
188
+ it "should call execute with path and method" do
189
+ expect(base).to receive(:execute).with(path, hash_including(method: :patch))
190
+ subject
191
+ end
192
+
193
+ it "should call execute with json null payload" do
194
+ expect(base).to receive(:execute).with(path, hash_including(payload: payload.to_json))
195
+ subject
196
+ end
197
+ end
198
+ end
199
+
200
+ context "without resource_path defined" do
201
+ it "should raise error" do
202
+ expect{ subject }.to raise_error("Must define resource_path in your subclass")
203
+ end
204
+ end
205
+ end
206
+
207
+
208
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'status_page'
3
+
4
+ describe StatusPage::API::ComponentList do
5
+ let(:page_id){ "zzzz" }
6
+ let(:component_list){ described_class.new page_id }
7
+
8
+ describe "get" do
9
+ subject { component_list.get }
10
+ # stub out request
11
+ let(:array_response) do
12
+ [
13
+ {"status"=>"operational", "name"=>"Library.nyu.edu", "id"=>"abcd", "page_id"=>"zzzz"},
14
+ {"status"=>"operational", "name"=>"E-Shelf", "id"=>"1234", "page_id"=>"zzzz"},
15
+ {"status"=>"operational", "name"=>"Login", "id"=>"wxyz", "page_id"=>"zzzz"},
16
+ ]
17
+ end
18
+ let(:component_class){ StatusPage::API::Component }
19
+ let(:component1){ double component_class, assign_attributes: true }
20
+ let(:component2){ double component_class, assign_attributes: true }
21
+ let(:component3){ double component_class, assign_attributes: true }
22
+ before do
23
+ allow(component_list).to receive(:get_resource).and_return array_response
24
+ allow(component_class).to receive(:new).and_return component1, component2, component3
25
+ end
26
+
27
+ it { is_expected.to be_a Array }
28
+ it { is_expected.to match_array [component1, component2, component3] }
29
+
30
+ it "should initialize each instance correctly" do
31
+ expect(component_class).to receive(:new).with("abcd", "zzzz").once
32
+ expect(component_class).to receive(:new).with("1234", "zzzz").once
33
+ expect(component_class).to receive(:new).with("wxyz", "zzzz").once
34
+ subject
35
+ end
36
+
37
+ it "should call assign_attributes on each instance" do
38
+ expect(component1).to receive(:assign_attributes).with(array_response[0])
39
+ expect(component2).to receive(:assign_attributes).with(array_response[1])
40
+ expect(component3).to receive(:assign_attributes).with(array_response[2])
41
+ subject
42
+ end
43
+
44
+ it "should persist array to instance" do
45
+ subject
46
+ expect(component_list).to match_array [component1, component2, component3]
47
+ end
48
+ end
49
+
50
+ describe "resource_path" do
51
+ subject { component_list.resource_path }
52
+ it { is_expected.to eq "pages/#{page_id}/components.json" }
53
+ end
54
+ end
@@ -0,0 +1,289 @@
1
+ require 'spec_helper'
2
+ require 'status_page'
3
+
4
+ describe StatusPage::API::Component do
5
+
6
+ describe "instance methods" do
7
+ let(:id){ "abcd" }
8
+ let(:page_id){ "wxyz" }
9
+ let(:component){ described_class.new id, page_id }
10
+
11
+ describe "id" do
12
+ subject { component.id }
13
+ it { is_expected.to eq id }
14
+ end
15
+
16
+ describe "page_id" do
17
+ subject { component.page_id }
18
+ it { is_expected.to eq page_id }
19
+ end
20
+
21
+ describe "get" do
22
+ subject { component.get }
23
+
24
+ context "with successful get" do
25
+ let(:result_attributes) do
26
+ {
27
+ "status"=>"operational",
28
+ "name"=>"Example site",
29
+ "created_at"=>"2015-07-14T19:41:51.042Z",
30
+ "updated_at"=>"2016-05-26T21:32:09.450Z",
31
+ "position"=>1,
32
+ "description"=>"Hello",
33
+ "id"=>"abcd",
34
+ "page_id"=>"wxyz",
35
+ "group_id"=>"nqrw"
36
+ }
37
+ end
38
+ before { allow(component).to receive(:get_resource).and_return result_attributes }
39
+
40
+ it { is_expected.to eq result_attributes }
41
+
42
+ context "after load" do
43
+ before { subject }
44
+ it "should assign id" do
45
+ expect(component.id).to eq "abcd"
46
+ end
47
+ it "should assign page_id" do
48
+ expect(component.page_id).to eq "wxyz"
49
+ end
50
+ it "should assign status" do
51
+ expect(component.status).to eq "operational"
52
+ end
53
+ it "should assign name" do
54
+ expect(component.name).to eq "Example site"
55
+ end
56
+ it "should assign created_at" do
57
+ expect(component.created_at).to eq "2015-07-14T19:41:51.042Z"
58
+ end
59
+ it "should assign updated_at" do
60
+ expect(component.updated_at).to eq "2016-05-26T21:32:09.450Z"
61
+ end
62
+ it "should assign position" do
63
+ expect(component.position).to eq 1
64
+ end
65
+ it "should assign description" do
66
+ expect(component.description).to eq "Hello"
67
+ end
68
+ it "should assign group_id" do
69
+ expect(component.group_id).to eq "nqrw"
70
+ end
71
+ end
72
+ end
73
+
74
+ context "with unsuccessful get" do
75
+ before { allow(component).to receive(:get_resource).and_raise RuntimeError, "some error" }
76
+
77
+ it "should raise that error" do
78
+ expect{ subject }.to raise_error RuntimeError, "some error"
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "save" do
84
+ subject { component.save }
85
+ let(:result_attributes) do
86
+ {
87
+ "status"=>"major_outage",
88
+ "name"=>"Example site",
89
+ "created_at"=>"2015-07-14T19:41:51.042Z",
90
+ "updated_at"=>"2016-05-26T21:32:09.450Z",
91
+ "position"=>4,
92
+ "description"=>"Hello",
93
+ "id"=>"abcd",
94
+ "page_id"=>"wxyz",
95
+ "group_id"=>"nqrw"
96
+ }
97
+ end
98
+ before { allow(component).to receive(:patch_resource).and_return result_attributes }
99
+
100
+ context "with attributes assigned" do
101
+ let(:attributes) do
102
+ {
103
+ "status"=>"major_outage",
104
+ "name"=>"Example site",
105
+ "created_at"=>"2015-07-14T19:41:51.042Z",
106
+ "updated_at"=>"2016-05-26T21:32:09.450Z",
107
+ "position"=>1,
108
+ "description"=>"Hello",
109
+ "id"=>"abcd",
110
+ "page_id"=>"wxyz",
111
+ "group_id"=>"nqrw"
112
+ }
113
+ end
114
+ before { component.send(:assign_attributes, attributes) }
115
+
116
+ context "with successful response" do
117
+ it { is_expected.to eq result_attributes }
118
+
119
+ it "should call patch_resource with correct hash" do
120
+ expect(component).to receive(:patch_resource).with({component: {status: "major_outage", description: "Hello", name: "Example site"}})
121
+ subject
122
+ end
123
+
124
+ it "should assign any changed attributes" do
125
+ subject
126
+ expect(component.position).to eq 4
127
+ end
128
+ end
129
+
130
+ context "with unsuccessful response" do
131
+ before { allow(component).to receive(:patch_resource).and_raise RuntimeError, "some error" }
132
+
133
+ it "should raise that error" do
134
+ expect{ subject }.to raise_error RuntimeError, "some error"
135
+ end
136
+ end
137
+ end
138
+
139
+ context "with attributes unassigned" do
140
+ it "should not call patch_resource" do
141
+ expect(component).to_not receive(:patch_resource)
142
+ subject
143
+ end
144
+ end
145
+ end
146
+
147
+ describe "failing?" do
148
+ subject { component.failing? }
149
+ context "when status operational" do
150
+ before { component.status = "operational" }
151
+ it { is_expected.to be_falsy }
152
+ end
153
+ context "when status not operational" do
154
+ before { component.status = "major_outage" }
155
+ it { is_expected.to be_truthy }
156
+ end
157
+ end
158
+
159
+ describe "status=" do
160
+ subject { component.status = status_type }
161
+ let(:status_type){ "major_outage" }
162
+
163
+ it "should assign status" do
164
+ subject
165
+ expect(component.status).to eq status_type
166
+ end
167
+
168
+ context "with invalid status" do
169
+ let(:status_type){ "xyz" }
170
+ it "should raise error" do
171
+ expect{ subject }.to raise_error "Status 'xyz' not recognized. Valid statuses: [\"operational\", \"degraded_performance\", \"partial_outage\", \"major_outage\"]"
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "resource_path" do
177
+ subject { component.resource_path }
178
+ it { is_expected.to eq "pages/#{page_id}/components/#{id}.json" }
179
+ end
180
+
181
+ describe "assign_attributes" do
182
+ subject { component.assign_attributes attributes }
183
+
184
+ context "given valid attributes with string keys" do
185
+ let(:attributes) do
186
+ {
187
+ "status"=>"operational",
188
+ "name"=>"Example site",
189
+ "created_at"=>"2015-07-14T19:41:51.042Z",
190
+ "updated_at"=>"2016-05-26T21:32:09.450Z",
191
+ "position"=>1,
192
+ "description"=>"Hello",
193
+ "id"=>"abcd",
194
+ "page_id"=>"wxyz",
195
+ "group_id"=>"nqrw"
196
+ }
197
+ end
198
+
199
+ it { is_expected.to eq attributes }
200
+
201
+ context "after call" do
202
+ before { subject }
203
+ it "should assign id" do
204
+ expect(component.id).to eq "abcd"
205
+ end
206
+ it "should assign page_id" do
207
+ expect(component.page_id).to eq "wxyz"
208
+ end
209
+ it "should assign status" do
210
+ expect(component.status).to eq "operational"
211
+ end
212
+ it "should assign name" do
213
+ expect(component.name).to eq "Example site"
214
+ end
215
+ it "should assign created_at" do
216
+ expect(component.created_at).to eq "2015-07-14T19:41:51.042Z"
217
+ end
218
+ it "should assign updated_at" do
219
+ expect(component.updated_at).to eq "2016-05-26T21:32:09.450Z"
220
+ end
221
+ it "should assign position" do
222
+ expect(component.position).to eq 1
223
+ end
224
+ it "should assign description" do
225
+ expect(component.description).to eq "Hello"
226
+ end
227
+ it "should assign group_id" do
228
+ expect(component.group_id).to eq "nqrw"
229
+ end
230
+ end
231
+ end
232
+
233
+ context "given valid attributes with symbol keys" do
234
+ let(:attributes) do
235
+ {
236
+ status: "operational",
237
+ name: "Example site",
238
+ created_at: "2015-07-14T19:41:51.042Z",
239
+ updated_at: "2016-05-26T21:32:09.450Z",
240
+ position: 1,
241
+ description: "Hello",
242
+ id: "abcd",
243
+ page_id: "wxyz",
244
+ group_id: "nqrw"
245
+ }
246
+ end
247
+
248
+ it { is_expected.to eq attributes }
249
+
250
+ context "after call" do
251
+ before { subject }
252
+ it "should assign id" do
253
+ expect(component.id).to eq "abcd"
254
+ end
255
+ it "should assign page_id" do
256
+ expect(component.page_id).to eq "wxyz"
257
+ end
258
+ it "should assign status" do
259
+ expect(component.status).to eq "operational"
260
+ end
261
+ it "should assign name" do
262
+ expect(component.name).to eq "Example site"
263
+ end
264
+ it "should assign created_at" do
265
+ expect(component.created_at).to eq "2015-07-14T19:41:51.042Z"
266
+ end
267
+ it "should assign updated_at" do
268
+ expect(component.updated_at).to eq "2016-05-26T21:32:09.450Z"
269
+ end
270
+ it "should assign position" do
271
+ expect(component.position).to eq 1
272
+ end
273
+ it "should assign description" do
274
+ expect(component.description).to eq "Hello"
275
+ end
276
+ it "should assign group_id" do
277
+ expect(component.group_id).to eq "nqrw"
278
+ end
279
+ end
280
+ end
281
+
282
+ context "given empty hash" do
283
+ let(:attributes){ {} }
284
+
285
+ it { is_expected.to eq attributes }
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'status_page'
3
+
4
+ describe StatusPage::API::Exception do
5
+ let(:exception){ described_class.new rest_client_error }
6
+
7
+ describe "message" do
8
+ subject{ exception.message }
9
+
10
+ before do
11
+ allow(exception).to receive(:response).and_return response
12
+ end
13
+
14
+ context "given response known to RestClient (422)" do
15
+ let(:rest_client_error){ RestClient::UnprocessableEntity.new }
16
+ let(:response){ "Something went wrong" }
17
+
18
+ it { is_expected.to eq "Unprocessable Entity (Something went wrong)" }
19
+ end
20
+
21
+ context "given response unknown to RestClient (420)" do
22
+ let(:rest_client_error){ RestClient::RequestFailed.new }
23
+ let(:response){ "Slow your roll" }
24
+ let(:http_code){ 420 }
25
+ before do
26
+ allow(rest_client_error).to receive(:http_code).and_return http_code
27
+ end
28
+
29
+ it { is_expected.to eq "HTTP status code 420 (Slow your roll)" }
30
+ end
31
+
32
+ context "given empty error response" do
33
+ let(:rest_client_error){ RestClient::ResourceNotFound.new }
34
+ let(:response){ "NO RESPONSE" }
35
+
36
+ it { is_expected.to eq "Not Found (NO RESPONSE)" }
37
+ end
38
+ end
39
+
40
+ describe "response" do
41
+ subject{ exception.response }
42
+
43
+ context "given response known to RestClient (422)" do
44
+ let(:rest_client_error){ RestClient::UnprocessableEntity.new }
45
+ let(:json_response){ {"error" => ["Something went wrong"]}.to_json }
46
+ before do
47
+ allow(rest_client_error).to receive(:response).and_return json_response
48
+ end
49
+
50
+ it { is_expected.to eq "Something went wrong" }
51
+ end
52
+
53
+ context "given response unknown to RestClient (420)" do
54
+ let(:rest_client_error){ RestClient::RequestFailed.new }
55
+ let(:json_response){ {"error" => "Slow your roll"}.to_json }
56
+ before do
57
+ allow(rest_client_error).to receive(:response).and_return json_response
58
+ end
59
+
60
+ it { is_expected.to eq "Slow your roll" }
61
+ end
62
+
63
+ context "given empty error response" do
64
+ let(:rest_client_error){ RestClient::ResourceNotFound.new }
65
+
66
+ it { is_expected.to eq "NO RESPONSE" }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,105 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+
20
+ require 'coveralls'
21
+ Coveralls.wear!
22
+ require 'support/modify_env'
23
+ require 'pry'
24
+
25
+ RSpec.configure do |config|
26
+
27
+ config.include ModifyEnv
28
+
29
+ # rspec-expectations config goes here. You can use an alternate
30
+ # assertion/expectation library such as wrong or the stdlib/minitest
31
+ # assertions if you prefer.
32
+ config.expect_with :rspec do |expectations|
33
+ # This option will default to `true` in RSpec 4. It makes the `description`
34
+ # and `failure_message` of custom matchers include text for helper methods
35
+ # defined using `chain`, e.g.:
36
+ # be_bigger_than(2).and_smaller_than(4).description
37
+ # # => "be bigger than 2 and smaller than 4"
38
+ # ...rather than:
39
+ # # => "be bigger than 2"
40
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
41
+ end
42
+
43
+ # rspec-mocks config goes here. You can use an alternate test double
44
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
45
+ config.mock_with :rspec do |mocks|
46
+ # Prevents you from mocking or stubbing a method that does not exist on
47
+ # a real object. This is generally recommended, and will default to
48
+ # `true` in RSpec 4.
49
+ mocks.verify_partial_doubles = true
50
+ end
51
+
52
+ # The settings below are suggested to provide a good initial experience
53
+ # with RSpec, but feel free to customize to your heart's content.
54
+ =begin
55
+ # These two settings work together to allow you to limit a spec run
56
+ # to individual examples or groups you care about by tagging them with
57
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
58
+ # get run.
59
+ config.filter_run :focus
60
+ config.run_all_when_everything_filtered = true
61
+
62
+ # Allows RSpec to persist some state between runs in order to support
63
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
64
+ # you configure your source control system to ignore this file.
65
+ config.example_status_persistence_file_path = "spec/examples.txt"
66
+
67
+ # Limits the available syntax to the non-monkey patched syntax that is
68
+ # recommended. For more details, see:
69
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
70
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
71
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
72
+ config.disable_monkey_patching!
73
+
74
+ # This setting enables warnings. It's recommended, but in some cases may
75
+ # be too noisy due to issues in dependencies.
76
+ config.warnings = true
77
+
78
+ # Many RSpec users commonly either run the entire suite or an individual
79
+ # file, and it's useful to allow more verbose output when running an
80
+ # individual spec file.
81
+ if config.files_to_run.one?
82
+ # Use the documentation formatter for detailed output,
83
+ # unless a formatter has already been configured
84
+ # (e.g. via a command-line flag).
85
+ config.default_formatter = 'doc'
86
+ end
87
+
88
+ # Print the 10 slowest examples and example groups at the
89
+ # end of the spec run, to help surface which specs are running
90
+ # particularly slow.
91
+ config.profile_examples = 10
92
+
93
+ # Run specs in random order to surface order dependencies. If you find an
94
+ # order dependency and want to debug it, you can fix the order by providing
95
+ # the seed, which is printed after each run.
96
+ # --seed 1234
97
+ config.order = :random
98
+
99
+ # Seed global randomization in this process using the `--seed` CLI option.
100
+ # Setting this allows you to use `--seed` to deterministically reproduce
101
+ # test failures related to randomization by passing the same `--seed` value
102
+ # as the one that triggered the failure.
103
+ Kernel.srand config.seed
104
+ =end
105
+ end
@@ -0,0 +1,7 @@
1
+ require 'climate_control'
2
+
3
+ module ModifyEnv
4
+ def with_modified_env(options, &block)
5
+ ClimateControl.modify(options, &block)
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'status_page/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'status_page-api'
7
+ s.version = StatusPage::VERSION
8
+ s.date = '2016-05-26'
9
+ s.summary = "Ruby client for StatusPage API"
10
+ s.description = "Ruby client for StatusPage API"
11
+ s.authors = ["Eric Griffis"]
12
+ s.email = 'eric.griffis@nyu.edu'
13
+
14
+ s.files = `git ls-files`.split($/)
15
+ s.test_files = %w[spec/lib/status_page/api/base_spec.rb spec/lib/status_page/api/component_spec.rb spec/lib/status_page/api/component_list_spec.rb]
16
+ s.require_paths = ["lib"]
17
+
18
+ s.homepage = 'https://github.com/NYULibraries/status_page'
19
+ s.license = 'MIT'
20
+
21
+ s.required_ruby_version = '>= 2.1'
22
+ s.add_dependency 'rest-client', '>= 2.0'
23
+ s.add_dependency 'json', '>= 1.0'
24
+
25
+ s.add_development_dependency 'rspec', '~> 3.5'
26
+ s.add_development_dependency 'climate_control', '~> 0.1'
27
+ s.add_development_dependency 'pry', '~> 0.10'
28
+ s.add_development_dependency 'coveralls', '~> 0.8'
29
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: status_page-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric Griffis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: climate_control
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: coveralls
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ description: Ruby client for StatusPage API
98
+ email: eric.griffis@nyu.edu
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - ".gitignore"
104
+ - ".rspec"
105
+ - ".ruby-version"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - README.md
109
+ - lib/status_page.rb
110
+ - lib/status_page/api.rb
111
+ - lib/status_page/api/base.rb
112
+ - lib/status_page/api/component.rb
113
+ - lib/status_page/api/component_list.rb
114
+ - lib/status_page/api/exception.rb
115
+ - lib/status_page/version.rb
116
+ - spec/lib/status_page/api/base_spec.rb
117
+ - spec/lib/status_page/api/component_list_spec.rb
118
+ - spec/lib/status_page/api/component_spec.rb
119
+ - spec/lib/status_page/api/exception_spec.rb
120
+ - spec/spec_helper.rb
121
+ - spec/support/modify_env.rb
122
+ - status_page.gemspec
123
+ homepage: https://github.com/NYULibraries/status_page
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '2.1'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.6.8
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Ruby client for StatusPage API
147
+ test_files:
148
+ - spec/lib/status_page/api/base_spec.rb
149
+ - spec/lib/status_page/api/component_spec.rb
150
+ - spec/lib/status_page/api/component_list_spec.rb