watchfor 0.14.0 → 0.16.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.
- checksums.yaml +4 -4
- data/README.md +59 -3
- data/exe/watchfor +24 -3
- data/lib/watchfor/client.rb +113 -10
- data/lib/watchfor/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3af4a4c0ee9b7d5b20fc4873a6710d37cc3d4c2d5197ff8a41fc108bc5c1bd38
|
|
4
|
+
data.tar.gz: 4ca5dea1cb43c7f1a2afc51c0eaf05048e1f5fd2db6f2545b4ebcc7d822e227b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ff5740ebc2e684b7aace9f419e0801b6e01d03c1770202a4670361023818c64ae2c43fbdb76787bffc714999411f6be30d7c765daaee73300ce04f1e9573a1e
|
|
7
|
+
data.tar.gz: 90f73616551e75ddf5e0093653609cd3375f7445d3bd1880b0159d0981cfd1947051037e908765feaa75bb7a2489cf2352a20cb150666fec3ed7c2727ca8ca0d
|
data/README.md
CHANGED
|
@@ -59,6 +59,19 @@ wf.monitors.checks(mon["id"], hours: 24, limit: 100, cursor: page["next_cursor"]
|
|
|
59
59
|
|
|
60
60
|
# Delete a contact group that alert rules still use (409 without force)
|
|
61
61
|
wf.contact_groups.delete("<group-id>", force: true)
|
|
62
|
+
|
|
63
|
+
# What the alerting engine sees right now: alerted, recovering 1/3, confirming 2 of 3…
|
|
64
|
+
state = wf.monitors.get(mon["id"])["alerting_state"]
|
|
65
|
+
|
|
66
|
+
# Response times on a real time axis (5-min buckets for 24h, 15 min for 7d)
|
|
67
|
+
up = wf.monitors.uptime(mon["id"], "24h", series: true)
|
|
68
|
+
(up["response_time_series"] || []).each { |b| puts "#{b['at']} #{b['avg_ms']}" }
|
|
69
|
+
|
|
70
|
+
# Pause up to 100 monitors at once; post a status-page announcement
|
|
71
|
+
wf.monitors.bulk("pause", ["<id-1>", "<id-2>"])
|
|
72
|
+
notice = wf.status_pages.create_announcement("<page-id>",
|
|
73
|
+
{ title: "Checkout errors", body: "We are looking into it." })
|
|
74
|
+
wf.status_pages.update_announcement("<page-id>", notice["id"], { body: "Fixed.", status: "resolved" })
|
|
62
75
|
```
|
|
63
76
|
|
|
64
77
|
### Live diagnostics
|
|
@@ -100,13 +113,49 @@ Runs need a `write` key (a probe sends real traffic from WatchFor's IPs)
|
|
|
100
113
|
and spend the same per-plan hourly allowance as the dashboard Toolbox.
|
|
101
114
|
Details: <https://watchfor.io/docs/api/diagnostics>
|
|
102
115
|
|
|
103
|
-
Namespaces: `wf.monitors`, `wf.alert_rules`, `wf.incidents`,
|
|
116
|
+
Namespaces: `wf.organizations`, `wf.monitors`, `wf.alert_rules`, `wf.incidents`,
|
|
104
117
|
`wf.hosts`, `wf.status_pages`, `wf.on_call`, `wf.notification_channels`, `wf.maintenance_windows`, `wf.contacts`, `wf.contact_groups`,
|
|
105
|
-
`wf.diagnostics`. Top-level:
|
|
118
|
+
`wf.diagnostics`, `wf.playwright`, `wf.variables`. Top-level:
|
|
106
119
|
`wf.summary`, `wf.plan`, `wf.me`, `wf.locations`, `wf.monitor_types`,
|
|
107
|
-
`wf.incident_stats(period)`, `wf.notifications`, `wf.activity
|
|
120
|
+
`wf.incident_stats(period)`, `wf.report`, `wf.notifications`, `wf.activity`,
|
|
121
|
+
`wf.batch([...])`, `wf.metrics`. Errors raise
|
|
108
122
|
`Watchfor::Error` with `#status`, `#code`, `#message`.
|
|
109
123
|
|
|
124
|
+
## Organizations
|
|
125
|
+
|
|
126
|
+
An API key belongs to one organization. An **OAuth access token** acts as
|
|
127
|
+
the user who approved it, so it reaches every organization that user
|
|
128
|
+
belongs to; choose one with `organization:` — the SDK sends it as the
|
|
129
|
+
`X-WatchFor-Organization` header on every request:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
wf = Watchfor::Client.new(api_key: oauth_access_token)
|
|
133
|
+
wf.organizations.list["data"].each do |org|
|
|
134
|
+
puts "#{org['id']} #{org['name']} #{org['role']} is_default=#{org['is_default']} is_current=#{org['is_current']}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
acme = Watchfor::Client.new(api_key: oauth_access_token, organization: "<organization-id>")
|
|
138
|
+
acme.me # {"organization" => {"id", "name", "slug"}, "scope" => ...}
|
|
139
|
+
other = acme.with_organization("<another-organization-id>") # new client, same token
|
|
140
|
+
other.monitors.list
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- `organizations.list` returns the ids to use: every organization of the
|
|
144
|
+
token's user with their `role`, `is_default` (used without `organization:`)
|
|
145
|
+
and `is_current` (what this client acts on). With an API key it returns just
|
|
146
|
+
the key's own organization (`role` is `nil`).
|
|
147
|
+
- Without `organization:`, an OAuth token acts on the user's oldest owned
|
|
148
|
+
organization (else their oldest membership).
|
|
149
|
+
- The user must be a member of that organization (otherwise `403
|
|
150
|
+
forbidden`), and their role there applies: members read, acknowledge
|
|
151
|
+
incidents and run diagnostics; changes need owner or admin.
|
|
152
|
+
- With an API key leave it unset; it may only repeat the key's own
|
|
153
|
+
organization id, and any other id is `403 forbidden` (a key never
|
|
154
|
+
switches organization).
|
|
155
|
+
- `with_organization(nil)` drops the header again.
|
|
156
|
+
|
|
157
|
+
Details: <https://watchfor.io/docs/api/authentication#how-oauth-maps-to-your-data>
|
|
158
|
+
|
|
110
159
|
## Prometheus metrics (Pro plan and above)
|
|
111
160
|
|
|
112
161
|
```ruby
|
|
@@ -124,6 +173,13 @@ watchfor monitors
|
|
|
124
173
|
watchfor incidents
|
|
125
174
|
watchfor checks <monitor_id> [--cursor <next_cursor>]
|
|
126
175
|
watchfor report --period 30d
|
|
176
|
+
watchfor me # which organization + scope
|
|
177
|
+
watchfor organizations # ids for --organization
|
|
178
|
+
|
|
179
|
+
# OAuth token: pick the organization per call or via the environment
|
|
180
|
+
export WATCHFOR_API_KEY=<oauth-access-token>
|
|
181
|
+
watchfor --organization <organization-id> monitors
|
|
182
|
+
WATCHFOR_ORGANIZATION=<organization-id> watchfor summary
|
|
127
183
|
```
|
|
128
184
|
|
|
129
185
|
Reference: <https://watchfor.io/openapi.json> · <https://watchfor.io/docs/api>
|
data/exe/watchfor
CHANGED
|
@@ -8,6 +8,22 @@ api_key = ENV["WATCHFOR_API_KEY"]
|
|
|
8
8
|
base_url = ENV.fetch("WATCHFOR_BASE_URL", Watchfor::DEFAULT_BASE_URL)
|
|
9
9
|
|
|
10
10
|
args = ARGV.dup
|
|
11
|
+
|
|
12
|
+
# Global --organization <id> / --organization=<id>, anywhere on the line;
|
|
13
|
+
# WATCHFOR_ORGANIZATION is the default. OAuth tokens reach every
|
|
14
|
+
# organization their user belongs to; an API key only accepts its own id.
|
|
15
|
+
organization = ENV.fetch("WATCHFOR_ORGANIZATION", nil)
|
|
16
|
+
if (i = args.index("--organization"))
|
|
17
|
+
organization = args[i + 1].to_s
|
|
18
|
+
args.slice!(i, 2)
|
|
19
|
+
if organization.strip.empty?
|
|
20
|
+
warn "--organization needs an organization id"
|
|
21
|
+
exit 2
|
|
22
|
+
end
|
|
23
|
+
elsif (i = args.index { |a| a.start_with?("--organization=") })
|
|
24
|
+
organization = args.delete_at(i).split("=", 2)[1].to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
11
27
|
command = args.shift
|
|
12
28
|
|
|
13
29
|
if command == "--version"
|
|
@@ -16,15 +32,17 @@ if command == "--version"
|
|
|
16
32
|
end
|
|
17
33
|
|
|
18
34
|
if api_key.nil? || api_key.empty?
|
|
19
|
-
warn "No API key. Set WATCHFOR_API_KEY."
|
|
35
|
+
warn "No API key. Set WATCHFOR_API_KEY (an API key or an OAuth access token)."
|
|
20
36
|
exit 2
|
|
21
37
|
end
|
|
22
38
|
|
|
23
|
-
wf = Watchfor::Client.new(api_key: api_key, base_url: base_url)
|
|
39
|
+
wf = Watchfor::Client.new(api_key: api_key, base_url: base_url, organization: organization)
|
|
24
40
|
|
|
25
41
|
begin
|
|
26
42
|
result =
|
|
27
43
|
case command
|
|
44
|
+
when "me" then wf.me
|
|
45
|
+
when "organizations", "orgs" then wf.organizations.list
|
|
28
46
|
when "summary" then wf.summary
|
|
29
47
|
when "plan" then wf.plan
|
|
30
48
|
when "monitors" then wf.monitors.list
|
|
@@ -42,7 +60,10 @@ begin
|
|
|
42
60
|
print wf.metrics(tag: tag, type: type)
|
|
43
61
|
exit 0
|
|
44
62
|
else
|
|
45
|
-
warn "Usage: watchfor [
|
|
63
|
+
warn "Usage: watchfor [--organization <id>] " \
|
|
64
|
+
"[me|organizations|summary|plan|monitors|locations|monitor-types|incidents|checks <monitor_id> [--cursor c]|" \
|
|
65
|
+
"report [--period 7d|30d]|metrics [--tag t] [--type t]]\n" \
|
|
66
|
+
"Env: WATCHFOR_API_KEY (API key or OAuth access token), WATCHFOR_ORGANIZATION, WATCHFOR_BASE_URL"
|
|
46
67
|
exit 2
|
|
47
68
|
end
|
|
48
69
|
puts JSON.pretty_generate(result)
|
data/lib/watchfor/client.rb
CHANGED
|
@@ -6,6 +6,8 @@ require "uri"
|
|
|
6
6
|
|
|
7
7
|
module Watchfor
|
|
8
8
|
DEFAULT_BASE_URL = "https://watchfor.io/api/v1"
|
|
9
|
+
# The request header that selects the organization an OAuth token acts on.
|
|
10
|
+
ORGANIZATION_HEADER = "X-WatchFor-Organization"
|
|
9
11
|
|
|
10
12
|
# Raised when the API returns a non-2xx response. Carries the HTTP
|
|
11
13
|
# +status+ and the API's structured +code+ and +message+.
|
|
@@ -24,17 +26,29 @@ module Watchfor
|
|
|
24
26
|
# wf = Watchfor::Client.new(api_key: "wf_live_...")
|
|
25
27
|
# wf.summary
|
|
26
28
|
# wf.monitors.list["data"].each { |m| puts "#{m['name']} #{m['status']}" }
|
|
29
|
+
#
|
|
30
|
+
# An OAuth access token reaches every organization its user belongs to;
|
|
31
|
+
# pick one with +organization:+ (sent as X-WatchFor-Organization):
|
|
32
|
+
#
|
|
33
|
+
# acme = Watchfor::Client.new(api_key: oauth_token, organization: "<org id>")
|
|
34
|
+
# other = acme.with_organization("<another org id>")
|
|
35
|
+
#
|
|
36
|
+
# An API key belongs to one organization: leave +organization:+ unset, or
|
|
37
|
+
# pass that same id (any other id is 403 forbidden).
|
|
27
38
|
class Client
|
|
28
|
-
attr_reader :monitors, :alert_rules, :incidents, :hosts, :status_pages, :on_call, :notification_channels,
|
|
39
|
+
attr_reader :organizations, :monitors, :alert_rules, :incidents, :hosts, :status_pages, :on_call, :notification_channels,
|
|
29
40
|
:maintenance_windows, :contacts, :contact_groups,
|
|
30
|
-
:diagnostics, :playwright, :variables
|
|
41
|
+
:diagnostics, :playwright, :variables, :organization
|
|
31
42
|
|
|
32
|
-
def initialize(api_key:, base_url: DEFAULT_BASE_URL, timeout: 30)
|
|
43
|
+
def initialize(api_key:, base_url: DEFAULT_BASE_URL, timeout: 30, organization: nil)
|
|
33
44
|
raise ArgumentError, "api_key is required" if api_key.nil? || api_key.empty?
|
|
34
45
|
|
|
35
46
|
@api_key = api_key
|
|
36
47
|
@base_url = base_url.chomp("/")
|
|
37
48
|
@timeout = timeout
|
|
49
|
+
org = organization.to_s.strip
|
|
50
|
+
@organization = org.empty? ? nil : org
|
|
51
|
+
@organizations = Organizations.new(self)
|
|
38
52
|
@monitors = Monitors.new(self)
|
|
39
53
|
@alert_rules = AlertRules.new(self)
|
|
40
54
|
@incidents = Incidents.new(self)
|
|
@@ -50,6 +64,14 @@ module Watchfor
|
|
|
50
64
|
@variables = Variables.new(self)
|
|
51
65
|
end
|
|
52
66
|
|
|
67
|
+
# A new client acting on another organization (same credential and
|
|
68
|
+
# settings); nil drops the header so the credential's default
|
|
69
|
+
# organization applies. This client is left unchanged.
|
|
70
|
+
def with_organization(organization)
|
|
71
|
+
self.class.new(api_key: @api_key, base_url: @base_url, timeout: @timeout,
|
|
72
|
+
organization: organization)
|
|
73
|
+
end
|
|
74
|
+
|
|
53
75
|
# Core request. +query+ and +body+ are optional hashes.
|
|
54
76
|
def request(method, path, query: nil, body: nil, idempotency_key: nil, text: false)
|
|
55
77
|
uri = URI("#{@base_url}#{path}")
|
|
@@ -68,6 +90,7 @@ module Watchfor
|
|
|
68
90
|
req["Accept"] = text ? "text/plain, application/json" : "application/json"
|
|
69
91
|
req["User-Agent"] = "watchfor-ruby/#{Watchfor::VERSION}"
|
|
70
92
|
req["Idempotency-Key"] = idempotency_key if idempotency_key
|
|
93
|
+
req[ORGANIZATION_HEADER] = @organization if @organization
|
|
71
94
|
unless body.nil?
|
|
72
95
|
req["Content-Type"] = "application/json"
|
|
73
96
|
req.body = JSON.generate(body)
|
|
@@ -92,7 +115,17 @@ module Watchfor
|
|
|
92
115
|
end
|
|
93
116
|
|
|
94
117
|
# Top-level reads
|
|
118
|
+
# The credential's scope and the organization it acts on (with an OAuth
|
|
119
|
+
# token, the one +organization:+ selected).
|
|
95
120
|
def me = request("GET", "/me")
|
|
121
|
+
|
|
122
|
+
# Run up to 20 operations ({ method:, path:, body: } with a relative
|
|
123
|
+
# /api/v1 path) in one request, with the same credential and
|
|
124
|
+
# organization; "results" come back in request order as {status, body}.
|
|
125
|
+
def batch(operations) = request("POST", "/batch", body: { operations: operations })
|
|
126
|
+
|
|
127
|
+
# The OpenAPI 3.1 document this client wraps (no key needed).
|
|
128
|
+
def openapi_spec = request("GET", "/openapi.json")
|
|
96
129
|
def summary = request("GET", "/summary")
|
|
97
130
|
|
|
98
131
|
# The organization in Prometheus text format (Pro plan and above):
|
|
@@ -108,11 +141,24 @@ module Watchfor
|
|
|
108
141
|
# Org report vs the previous period (period: "7d"/"30d", or from/to ISO 8601
|
|
109
142
|
# on advanced-reports plans): uptime, incidents, MTTR/MTBF, response-time
|
|
110
143
|
# trend, per-monitor rows, type-aware sections and plain-language insights.
|
|
111
|
-
|
|
144
|
+
#
|
|
145
|
+
# Scoping (advanced-reports plans): monitor_ids:/tags: (union), sections:
|
|
146
|
+
# (locations, ssl, domains, cwv, heartbeat, blacklist, mcp, hosts),
|
|
147
|
+
# incident_status: (acknowledged, resolved, open) — arrays or
|
|
148
|
+
# comma-separated strings. false for response_time:/monitors_table:/
|
|
149
|
+
# insights: drops that block.
|
|
150
|
+
def report(period: nil, from: nil, to: nil, monitor_ids: nil, tags: nil, sections: nil,
|
|
151
|
+
response_time: nil, monitors_table: nil, insights: nil, incident_status: nil)
|
|
152
|
+
csv = ->(v) { v.is_a?(Array) ? v.join(",") : v }
|
|
153
|
+
query = { monitor_ids: csv.call(monitor_ids), tags: csv.call(tags), sections: csv.call(sections),
|
|
154
|
+
incident_status: csv.call(incident_status),
|
|
155
|
+
response_time: response_time.nil? ? nil : response_time.to_s,
|
|
156
|
+
monitors_table: monitors_table.nil? ? nil : monitors_table.to_s,
|
|
157
|
+
insights: insights.nil? ? nil : insights.to_s }
|
|
112
158
|
if from || to
|
|
113
|
-
request("GET", "/reports", query:
|
|
159
|
+
request("GET", "/reports", query: query.merge(from: from, to: to))
|
|
114
160
|
else
|
|
115
|
-
request("GET", "/reports", query:
|
|
161
|
+
request("GET", "/reports", query: query.merge(period: period))
|
|
116
162
|
end
|
|
117
163
|
end
|
|
118
164
|
# The org's two report-email cadences (weekly + monthly); set toggles
|
|
@@ -131,8 +177,17 @@ module Watchfor
|
|
|
131
177
|
|
|
132
178
|
class Monitors < Namespace
|
|
133
179
|
def list(**query) = @c.request("GET", "/monitors", query: query)
|
|
134
|
-
# The monitor plus available_metrics (alert metrics valid for its type)
|
|
180
|
+
# The monitor plus available_metrics (alert metrics valid for its type)
|
|
181
|
+
# and alerting_state — what the alerting engine sees right now (alerted,
|
|
182
|
+
# recovering n of m, failing/confirming n of m; nil when every rule is
|
|
183
|
+
# healthy).
|
|
135
184
|
def get(id) = @c.request("GET", "/monitors/#{id}")
|
|
185
|
+
# Pause, resume or delete (+action+) up to 100 monitors in one call; ids
|
|
186
|
+
# that are not yours or need no change come back in "skipped".
|
|
187
|
+
def bulk(action, ids, idempotency_key: nil)
|
|
188
|
+
@c.request("POST", "/monitors/bulk", body: { action: action, ids: ids },
|
|
189
|
+
idempotency_key: idempotency_key)
|
|
190
|
+
end
|
|
136
191
|
# Credentials (catalog fields marked secret: mailbox/SMTP/FTP passwords,
|
|
137
192
|
# SSH keys, HTTP/API/MCP auth passwords, bearer tokens and credential
|
|
138
193
|
# header values) are write-only: stored encrypted, returned as
|
|
@@ -142,7 +197,9 @@ module Watchfor
|
|
|
142
197
|
# config is merged (omitted keys kept, nil removes). A "[redacted]"
|
|
143
198
|
# credential is kept when omitted or sent back; changing the server,
|
|
144
199
|
# port, TLS mode or login it is used with (HTTP: the URL's host, port
|
|
145
|
-
# or scheme) needs it again (else 400).
|
|
200
|
+
# or scheme) needs it again (else 400). "check_queued" => true in the
|
|
201
|
+
# result means the change dispatched one immediate check, outside the
|
|
202
|
+
# Run-now budget.
|
|
146
203
|
def update(id, body) = @c.request("PATCH", "/monitors/#{id}", body: body)
|
|
147
204
|
def delete(id) = @c.request("DELETE", "/monitors/#{id}")
|
|
148
205
|
def pause(id) = @c.request("POST", "/monitors/#{id}/pause")
|
|
@@ -153,7 +210,13 @@ module Watchfor
|
|
|
153
210
|
# monitor and starts uptime over ("since reset"); the monitor and its
|
|
154
211
|
# configuration, alert rules, tags and locations stay.
|
|
155
212
|
def reset(id) = @c.request("POST", "/monitors/#{id}/reset")
|
|
156
|
-
|
|
213
|
+
# Uptime (confirmed critical incidents only), check success and
|
|
214
|
+
# response-time stats. series: true (periods up to 7d) adds
|
|
215
|
+
# "response_time_series" — buckets of "series_bucket_seconds" (300 for
|
|
216
|
+
# 24h, 900 for 7d) on a real time axis, oldest first.
|
|
217
|
+
def uptime(id, period = nil, series: false)
|
|
218
|
+
@c.request("GET", "/monitors/#{id}/uptime", query: { period: period, series: series ? "true" : nil })
|
|
219
|
+
end
|
|
157
220
|
# Recent probe results, newest first. Page with the response's next_cursor -> cursor:
|
|
158
221
|
# (keep the same hours/success); "locations" is the per-location breakdown for the whole window.
|
|
159
222
|
def checks(id, hours: nil, limit: nil, success: nil, cursor: nil, **query)
|
|
@@ -206,10 +269,21 @@ module Watchfor
|
|
|
206
269
|
def delete(name) = @c.request("DELETE", "/variables/#{name}")
|
|
207
270
|
end
|
|
208
271
|
|
|
272
|
+
# The organizations this credential can act on: the ids +organization:+ /
|
|
273
|
+
# +with_organization+ accept. An OAuth token lists every organization of
|
|
274
|
+
# its user ("role", plus "is_default" for the one used without
|
|
275
|
+
# +organization:+ and "is_current" for the one the request acted on); an
|
|
276
|
+
# API key only its own ("role" nil).
|
|
277
|
+
class Organizations < Namespace
|
|
278
|
+
def list = @c.request("GET", "/organizations")
|
|
279
|
+
end
|
|
280
|
+
|
|
209
281
|
class AlertRules < Namespace
|
|
210
282
|
def list(monitor_id) = @c.request("GET", "/monitors/#{monitor_id}/alert-rules")
|
|
211
283
|
def get(monitor_id, rule_id) = @c.request("GET", "/monitors/#{monitor_id}/alert-rules/#{rule_id}")
|
|
212
|
-
def create(monitor_id, body
|
|
284
|
+
def create(monitor_id, body, idempotency_key: nil)
|
|
285
|
+
@c.request("POST", "/monitors/#{monitor_id}/alert-rules", body: body, idempotency_key: idempotency_key)
|
|
286
|
+
end
|
|
213
287
|
def update(monitor_id, rule_id, body) = @c.request("PATCH", "/monitors/#{monitor_id}/alert-rules/#{rule_id}", body: body)
|
|
214
288
|
def delete(monitor_id, rule_id) = @c.request("DELETE", "/monitors/#{monitor_id}/alert-rules/#{rule_id}")
|
|
215
289
|
end
|
|
@@ -225,12 +299,40 @@ module Watchfor
|
|
|
225
299
|
def exclude(id, reason: nil) = @c.request("POST", "/incidents/#{id}/exclude", body: reason.nil? ? {} : { reason: reason })
|
|
226
300
|
# Undo #exclude - the incident counts as downtime again.
|
|
227
301
|
def include(id) = @c.request("POST", "/incidents/#{id}/include")
|
|
302
|
+
# Retroactively count a RESOLVED incident as planned maintenance: it
|
|
303
|
+
# leaves uptime and SLA. Returns the "maintenance_window" recorded for it
|
|
304
|
+
# (marking again returns the same one); delete that window to undo.
|
|
305
|
+
def mark_maintenance(id, idempotency_key: nil)
|
|
306
|
+
@c.request("POST", "/incidents/#{id}/mark-maintenance", idempotency_key: idempotency_key)
|
|
307
|
+
end
|
|
228
308
|
end
|
|
229
309
|
|
|
230
310
|
class StatusPages < Namespace
|
|
231
311
|
def list(**query) = @c.request("GET", "/status-pages", query: query)
|
|
232
312
|
def get(id) = @c.request("GET", "/status-pages/#{id}")
|
|
313
|
+
# Create a page (slug, name, optional components...). Password protection,
|
|
314
|
+
# logo upload and custom subdomains are dashboard-only.
|
|
315
|
+
def create(body, idempotency_key: nil) = @c.request("POST", "/status-pages", body: body, idempotency_key: idempotency_key)
|
|
316
|
+
# Partial update; a slug change frees the old URL at once.
|
|
317
|
+
def update(id, body) = @c.request("PATCH", "/status-pages/#{id}", body: body)
|
|
318
|
+
def delete(id) = @c.request("DELETE", "/status-pages/#{id}")
|
|
319
|
+
# Announcements, newest first.
|
|
233
320
|
def announcements(id, **query) = @c.request("GET", "/status-pages/#{id}/announcements", query: query)
|
|
321
|
+
def get_announcement(id, announcement_id) = @c.request("GET", "/status-pages/#{id}/announcements/#{announcement_id}")
|
|
322
|
+
# Publish an incident notice (title, body, optional status, severity,
|
|
323
|
+
# affectedComponentIds); body becomes the first timeline entry.
|
|
324
|
+
def create_announcement(id, body, idempotency_key: nil)
|
|
325
|
+
@c.request("POST", "/status-pages/#{id}/announcements", body: body, idempotency_key: idempotency_key)
|
|
326
|
+
end
|
|
327
|
+
# Post an update (body + status): appends to the public timeline;
|
|
328
|
+
# "resolved" closes it.
|
|
329
|
+
def update_announcement(id, announcement_id, body)
|
|
330
|
+
@c.request("PATCH", "/status-pages/#{id}/announcements/#{announcement_id}", body: body)
|
|
331
|
+
end
|
|
332
|
+
# Remove from the public page (soft delete).
|
|
333
|
+
def delete_announcement(id, announcement_id)
|
|
334
|
+
@c.request("DELETE", "/status-pages/#{id}/announcements/#{announcement_id}")
|
|
335
|
+
end
|
|
234
336
|
end
|
|
235
337
|
|
|
236
338
|
class OnCall < Namespace
|
|
@@ -249,6 +351,7 @@ module Watchfor
|
|
|
249
351
|
end
|
|
250
352
|
|
|
251
353
|
class MaintenanceWindows < Namespace
|
|
354
|
+
# state: "active" / "upcoming" keeps only live windows.
|
|
252
355
|
def list(**query) = @c.request("GET", "/maintenance-windows", query: query)
|
|
253
356
|
def get(id) = @c.request("GET", "/maintenance-windows/#{id}")
|
|
254
357
|
def create(body, idempotency_key: nil) = @c.request("POST", "/maintenance-windows", body: body, idempotency_key: idempotency_key)
|
data/lib/watchfor/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: watchfor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- WatchFor
|
|
@@ -24,11 +24,11 @@ files:
|
|
|
24
24
|
- lib/watchfor.rb
|
|
25
25
|
- lib/watchfor/client.rb
|
|
26
26
|
- lib/watchfor/version.rb
|
|
27
|
-
homepage: https://watchfor.io
|
|
27
|
+
homepage: https://watchfor.io
|
|
28
28
|
licenses:
|
|
29
29
|
- MIT
|
|
30
30
|
metadata:
|
|
31
|
-
homepage_uri: https://watchfor.io
|
|
31
|
+
homepage_uri: https://watchfor.io
|
|
32
32
|
documentation_uri: https://watchfor.io/docs/api
|
|
33
33
|
changelog_uri: https://watchfor.io/changelog
|
|
34
34
|
rubygems_mfa_required: 'true'
|