status_page-api 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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