Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ In this guide:
- [Monitoring Background Jobs](#monitoring-background-jobs)
- [Sending Telemetry Events](#sending-telemetry-events)
- [Configuring Monitors](#configuring-monitors)
- [Fetching Status Badges](#fetching-status-badges)
- [Package Configuration & Env Vars](#package-configuration)
- [Contributing](#contributing)

Expand Down Expand Up @@ -203,6 +204,42 @@ monitor.ok # manually reset to a passing state alias for monitor.ping({state: ok
monitor.delete # destroy the monitor
```

## Fetching Status Badges

[Status badges](https://cronitor.io/docs/status-badges) provide a visual indicator of your monitor's health. Badges are indexed by tag, so to get a badge for a specific monitor, you must first assign it a unique tag.

```ruby
require 'cronitor'
Cronitor.api_key = 'api_key_123'

# Fetch all badges
badges = Cronitor::Badge.all

# Each badge is indexed by its tag
badges.each do |tag, badge|
puts "Tag: #{tag}"
puts "SVG URL: #{badge.svg_url}"
puts "Badge Key: #{badge.key}"
end

# Access a specific badge by tag
if badge = badges['my-monitor-tag']
# Extract the badge key for building embed URLs
key = badge.key # e.g., 'abc123xyz'

# Build your own embed URL
# Standard: https://cronitor.io/badges/ACCOUNT_ID/production/KEY.svg
# Detailed: https://cronitor.io/badges/ACCOUNT_ID/production/KEY/detailed.svg
end
```

### Badge Attributes

- `tag` - The tag name used to index this badge
- `svg_url` - Direct URL to the badge SVG image
- `url` - Alternative URL for the badge
- `key` - The unique badge key extracted from the SVG URL (useful for constructing embed URLs)

## Package Configuration

The package needs to be configured with your account's `API key`, which is available on the [account settings](https://cronitor.io/settings) page. You can also optionally specify an `api_version` and an `environment`. If not provided, your account default is used. These can also be supplied using the environment variables `CRONITOR_API_KEY`, `CRONITOR_API_VERSION`, `CRONITOR_ENVIRONMENT`.
Expand Down
1 change: 1 addition & 0 deletions lib/cronitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'cronitor/error'
require 'cronitor/version'
require 'cronitor/monitor'
require 'cronitor/badge'

module Cronitor
def self.read_config(path = nil)
Expand Down
81 changes: 81 additions & 0 deletions lib/cronitor/badge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

module Cronitor
class Badge
BADGE_API_URL = 'https://cronitor.io/api/badges'

# Fetch all badges from the Cronitor API
# Returns a hash where keys are tag names and values are Badge objects
#
# @param api_key [String] Optional API key (defaults to Cronitor.api_key)
# @param api_version [String] Optional API version header
# @return [Hash<String, Badge>] Hash of tag => Badge objects
# @raise [Cronitor::Error] If API key is missing or API returns an error
#
# @example
# Cronitor.api_key = 'your-api-key'
# badges = Cronitor::Badge.all
# badges.each do |tag, badge|
# puts "#{tag}: #{badge.svg_url}"
# puts "Badge key: #{badge.key}"
# end
def self.all(api_key: nil, api_version: nil)
api_key ||= Cronitor.api_key

unless api_key
raise Error.new('No API key detected. Set Cronitor.api_key or pass api_key parameter')
end

headers = Monitor::Headers.json
headers[:'Cronitor-Version'] = api_version if api_version

resp = HTTParty.get(
BADGE_API_URL,
basic_auth: {
username: api_key,
password: ''
},
headers: headers,
timeout: Cronitor.timeout || 10
)

case resp.code
when 200
data = JSON.parse(resp.body)
badges = {}
data.each do |tag, badge_data|
badges[tag] = Badge.new(
tag: tag,
svg_url: badge_data['svg'],
url: badge_data['url']
)
end
badges
else
raise Error.new("Error fetching badges: #{resp.code} - #{resp.body}")
end
end

attr_reader :tag, :svg_url, :url

def initialize(tag:, svg_url: nil, url: nil)
@tag = tag
@svg_url = svg_url || url
@url = url || svg_url
end

# Extract the badge key from the SVG URL
# Badge URLs follow these patterns:
# Standard: https://cronitor.io/badges/ACCOUNT/production/KEY.svg
# Detailed: https://cronitor.io/badges/ACCOUNT/production/KEY/detailed.svg
#
# @return [String, nil] The badge key or nil if URL doesn't match expected pattern
def key
return nil unless svg_url

# Match the key segment after /production/ - stops at next / or .
match = svg_url.match(%r{/production/([^/.]+)})
match&.[](1)
end
end
end
33 changes: 18 additions & 15 deletions lib/cronitor/monitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ module Formats
end

module Headers
JSON = {
'Content-Type': 'application/json',
'User-Agent': "cronitor-ruby-#{Cronitor::VERSION}",
'Cronitor-Version': Cronitor.api_version
}.freeze
YAML = JSON.merge({
'Content-Type': 'application/yaml'
})
def self.json
{
'Content-Type': 'application/json',
'User-Agent': "cronitor-ruby-#{Cronitor::VERSION}",
'Cronitor-Version': Cronitor.api_version
}
end

def self.yaml
json.merge('Content-Type': 'application/yaml')
end
end

def self.put(opts = {})
Expand All @@ -35,13 +38,13 @@ def self.put(opts = {})
url = "#{url}.yaml"
monitors['rollback'] = true if rollback
body = YAML.dump(monitors)
headers = Cronitor::Monitor::Headers::YAML
headers = Cronitor::Monitor::Headers.yaml
else
body = {
monitors: monitors,
rollback: rollback
}.to_json
headers = Cronitor::Monitor::Headers::JSON
headers = Cronitor::Monitor::Headers.json
end

resp = HTTParty.put(
Expand Down Expand Up @@ -85,7 +88,7 @@ def self.as_yaml(api_key: nil, api_version: nil)
raise Error.new('No API key detected. Set Cronitor.api_key or pass api_key parameter')
end

headers = Cronitor::Monitor::Headers::YAML.dup
headers = Cronitor::Monitor::Headers.yaml
headers[:'Cronitor-Version'] = api_version if api_version

resp = HTTParty.get(
Expand Down Expand Up @@ -114,7 +117,7 @@ def delete
username: api_key,
password: ''
},
headers: Cronitor::Monitor::Headers::JSON
headers: Cronitor::Monitor::Headers.json
)
if resp.code != 204
Cronitor.logger&.error("Error deleting monitor: #{key}")
Expand Down Expand Up @@ -154,7 +157,7 @@ def ping(params = {})
ping_url,
query: clean_params(params),
timeout: Cronitor.ping_timeout,
headers: Cronitor::Monitor::Headers::JSON,
headers: Cronitor::Monitor::Headers.json,
query_string_normalizer: lambda do |query|
query.compact!
metrics = query[:metric]
Expand Down Expand Up @@ -193,7 +196,7 @@ def pause(hours = nil)
resp = HTTParty.get(
pause_url,
timeout: Cronitor.timeout,
headers: Cronitor::Monitor::Headers::JSON,
headers: Cronitor::Monitor::Headers.json,
basic_auth: {
username: api_key,
password: ''
Expand Down Expand Up @@ -236,7 +239,7 @@ def fetch
password: ''
},
timeout: Cronitor.timeout,
headers: Cronitor::Monitor::Headers::JSON,
headers: Cronitor::Monitor::Headers.json,
format: :json
)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/cronitor/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Cronitor
VERSION = '5.3.0'
VERSION = '5.4.0'
end
Loading