apialerts 1.0.0.alpha.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ca74872675531d5f22e9ae62c2298a760e7c2ada9c33f8f00b9de4109dd7c0f7
4
+ data.tar.gz: 7bd96af6093b6b5f2e74bba74f1e887cec60a9c728edfcdf46751e7d24a0e811
5
+ SHA512:
6
+ metadata.gz: 1957c3f75ac52982b322501b35082783c5be24046d336f68724a99d269e9cba1a66ced602927c180642303632a32cc99d79d9ca9e03025fa4c5ee2a29f9ff10d
7
+ data.tar.gz: 0ab4395dd514436e03c6e9c35bdbcfb4e8f77a636b6449974617df69b106c905d45434d5f2dc1ee3da517dee0553b966255e6c6a5d3768d0dca22731e8102385
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 API Alerts
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # API Alerts • Ruby Client
2
+
3
+ [![Gem](https://img.shields.io/gem/v/apialerts)](https://rubygems.org/gems/apialerts)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ [RubyGems](https://rubygems.org/gems/apialerts) • [GitHub](https://github.com/apialerts/apialerts-ruby) • [API Alerts](https://apialerts.com)
7
+
8
+ Effortless project notifications. Send once, deliver everywhere.
9
+
10
+ ## Installation
11
+
12
+ ```ruby
13
+ gem 'apialerts'
14
+ ```
15
+
16
+ Or install directly:
17
+
18
+ ```bash
19
+ gem install apialerts
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```ruby
25
+ require 'apialerts'
26
+
27
+ ApiAlerts.configure('your-api-key')
28
+ ApiAlerts.send(ApiAlerts::Event.new(message: 'Deploy complete'))
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Global singleton (recommended)
34
+
35
+ Call `configure` once at startup, then use `send` / `send_async` anywhere.
36
+
37
+ ```ruby
38
+ require 'apialerts'
39
+
40
+ ApiAlerts.configure('your-api-key')
41
+
42
+ # Fire-and-forget - never raises
43
+ ApiAlerts.send(ApiAlerts::Event.new(message: 'Deploy complete'))
44
+
45
+ # Or get the result back
46
+ result = ApiAlerts.send_async(ApiAlerts::Event.new(message: 'Deploy complete'))
47
+ if result.success?
48
+ puts "Sent to #{result.workspace} (#{result.channel})"
49
+ else
50
+ puts "Error: #{result.error}"
51
+ end
52
+ ```
53
+
54
+ ### Event fields
55
+
56
+ Only `message` is required. All other fields are optional.
57
+
58
+ | Field | Type | Required | Description |
59
+ |-----------|----------|----------|----------------------------------|
60
+ | `message` | `String` | Yes | Main notification message |
61
+ | `channel` | `String` | No | Target channel name |
62
+ | `event` | `String` | No | Event key for routing |
63
+ | `title` | `String` | No | Short title |
64
+ | `tags` | `Array` | No | Categorisation tags |
65
+ | `link` | `String` | No | URL associated with the event (deeplink + CTA) |
66
+ | `data` | `Hash` | No | Arbitrary key-value metadata |
67
+
68
+ ```ruby
69
+ event = ApiAlerts::Event.new(
70
+ message: 'Deploy complete',
71
+ channel: 'releases',
72
+ event: 'ci.deploy',
73
+ title: 'Deployed',
74
+ tags: ['CI/CD', 'Ruby'],
75
+ link: 'https://github.com/apialerts/apialerts-ruby/actions',
76
+ data: { version: '2.0.0' }
77
+ )
78
+ ```
79
+
80
+ ### Send to a different workspace
81
+
82
+ Pass an optional `api_key:` to override the configured key for a single call.
83
+
84
+ ```ruby
85
+ ApiAlerts.send(event, api_key: 'other-workspace-key')
86
+
87
+ result = ApiAlerts.send_async(event, api_key: 'other-workspace-key')
88
+ ```
89
+
90
+ ### SendResult fields
91
+
92
+ `send_async` always returns a `SendResult` - it never raises.
93
+
94
+ | Field | Type | Description |
95
+ |-------------|-----------|------------------------------------------------|
96
+ | `success?` | `Boolean` | Whether the event was delivered successfully |
97
+ | `workspace` | `String` | Workspace the event was delivered to |
98
+ | `channel` | `String` | Channel the event was delivered to |
99
+ | `warnings` | `Array` | Non-fatal warnings returned by the API |
100
+ | `error` | `String` | Error message when `success?` is false |
101
+
102
+ ## Links
103
+
104
+ - [Documentation](https://apialerts.com/docs)
105
+ - [Sign up](https://apialerts.com)
106
+ - [GitHub Issues](https://github.com/apialerts/apialerts-ruby/issues)
@@ -0,0 +1,111 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ require_relative 'event'
6
+ require_relative 'result'
7
+ require_relative 'version'
8
+
9
+ module ApiAlerts
10
+ # Instance-based API Alerts client.
11
+ #
12
+ # Use the module-level methods (ApiAlerts.configure / ApiAlerts.send_async)
13
+ # for a convenient global singleton, or construct this class directly when
14
+ # you need multiple clients or full lifecycle control.
15
+ class Client
16
+ def initialize(api_key, debug: false)
17
+ @api_key = api_key
18
+ @debug = debug
19
+ @integration = INTEGRATION_NAME
20
+ @integration_version = VERSION
21
+ @base_url = API_URL
22
+ end
23
+
24
+ # Override the integration name, version, and base URL.
25
+ #
26
+ # Used by official integrations and in tests to redirect requests to a
27
+ # mock server.
28
+ def set_overrides(integration, version, base_url)
29
+ @integration = integration
30
+ @integration_version = version
31
+ @base_url = base_url
32
+ self
33
+ end
34
+
35
+ # Send an event - fire-and-forget. Never raises.
36
+ # Critical errors (not configured, missing key, empty message) are always
37
+ # logged to stderr. HTTP errors and success are only logged when debug is
38
+ # enabled.
39
+ def send(event, api_key: nil)
40
+ key = api_key.nil? || api_key.empty? ? @api_key : api_key
41
+ if key.nil? || key.empty?
42
+ warn 'x (apialerts.com) Error: api key is missing'
43
+ return
44
+ end
45
+ if event.message.nil? || event.message.empty?
46
+ warn 'x (apialerts.com) Error: message is required'
47
+ return
48
+ end
49
+
50
+ result = post(event, api_key: key)
51
+ return unless @debug
52
+
53
+ if result.success?
54
+ warn "✓ (apialerts.com) Alert sent to #{result.workspace} (#{result.channel})"
55
+ result.warnings.each { |w| warn "! (apialerts.com) Warning: #{w}" }
56
+ else
57
+ warn "x (apialerts.com) Error: #{result.error}"
58
+ end
59
+ end
60
+
61
+ # Send an event and return a SendResult. Never raises.
62
+ # Check result.success? to determine whether delivery succeeded.
63
+ def send_async(event, api_key: nil)
64
+ key = api_key.nil? || api_key.empty? ? @api_key : api_key
65
+ return SendResult.new(success: false, error: 'api key is missing') if key.nil? || key.empty?
66
+ return SendResult.new(success: false, error: 'message is required') if event.message.nil? || event.message.empty?
67
+
68
+ post(event, api_key: key)
69
+ end
70
+
71
+ private
72
+
73
+ def post(event, api_key: @api_key)
74
+ uri = URI.parse(@base_url)
75
+ http = Net::HTTP.new(uri.host, uri.port).tap do |h|
76
+ h.use_ssl = uri.scheme == 'https'
77
+ h.open_timeout = TIMEOUT_SECONDS
78
+ h.read_timeout = TIMEOUT_SECONDS
79
+ end
80
+ request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
81
+
82
+ request['Authorization'] = "Bearer #{api_key}"
83
+ request['Content-Type'] = 'application/json'
84
+ request['X-Integration'] = @integration
85
+ request['X-Version'] = @integration_version
86
+ request.body = JSON.generate(event.to_h)
87
+
88
+ response = http.request(request)
89
+
90
+ case response.code.to_i
91
+ when 200
92
+ body = JSON.parse(response.body)
93
+ workspace = body['workspace']
94
+ channel = body['channel']
95
+ return SendResult.new(success: false, error: 'invalid response from server') unless workspace && channel
96
+
97
+ warnings = body['warnings'] || []
98
+ SendResult.new(success: true, workspace: workspace, channel: channel, warnings: warnings)
99
+ when 400 then SendResult.new(success: false, error: 'bad request')
100
+ when 401 then SendResult.new(success: false, error: 'unauthorized, check your api key')
101
+ when 403 then SendResult.new(success: false, error: 'forbidden')
102
+ when 429 then SendResult.new(success: false, error: 'rate limit exceeded')
103
+ else SendResult.new(success: false, error: "unexpected status: #{response.code.to_i}")
104
+ end
105
+ rescue JSON::ParserError
106
+ SendResult.new(success: false, error: 'invalid response from server')
107
+ rescue StandardError => e
108
+ SendResult.new(success: false, error: e.message)
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,59 @@
1
+ module ApiAlerts
2
+ # An event to send to the API Alerts platform.
3
+ #
4
+ # Only +message+ is required. All other fields are optional and omitted
5
+ # from the JSON payload when nil.
6
+ #
7
+ # # Minimal
8
+ # event = ApiAlerts::Event.new(message: 'Deploy complete')
9
+ #
10
+ # # Full
11
+ # event = ApiAlerts::Event.new(
12
+ # message: 'Deploy complete',
13
+ # channel: 'releases',
14
+ # event: 'ci.deploy.success',
15
+ # title: 'Deployed',
16
+ # tags: ['CI/CD', 'Ruby'],
17
+ # link: 'https://github.com/apialerts/apialerts-ruby/actions',
18
+ # data: { commit: 'a1b2c3d' }
19
+ # )
20
+ class Event
21
+ # Human-readable notification text. Required. Appears on the push lock screen.
22
+ attr_reader :message
23
+ # Workspace channel the push fires on. Defaults to the workspace default when omitted.
24
+ attr_reader :channel
25
+ # What kind of thing happened. Optional but recommended. Dotted notation
26
+ # ("ci.deploy.success", "payment.failed") so routing rules can match with
27
+ # wildcards ("ci.*", "*.failed").
28
+ attr_reader :event
29
+ # Short headline some destinations render separately from the body.
30
+ attr_reader :title
31
+ # Categorisation tags for filtering and search.
32
+ attr_reader :tags
33
+ # URL attached to the notification. Tapping the push opens it.
34
+ attr_reader :link
35
+ # Arbitrary key-value metadata. Available to non-push destinations for templating.
36
+ attr_reader :data
37
+
38
+ def initialize(message:, channel: nil, event: nil, title: nil, tags: nil, link: nil, data: nil)
39
+ @message = message
40
+ @channel = channel
41
+ @event = event
42
+ @title = title
43
+ @tags = tags
44
+ @link = link
45
+ @data = data
46
+ end
47
+
48
+ def to_h
49
+ h = { message: message }
50
+ h[:channel] = channel unless channel.nil?
51
+ h[:event] = event unless event.nil?
52
+ h[:title] = title unless title.nil?
53
+ h[:tags] = tags unless tags.nil?
54
+ h[:link] = link unless link.nil?
55
+ h[:data] = data unless data.nil?
56
+ h
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ module ApiAlerts
2
+ # The result of an event delivery attempt.
3
+ class SendResult
4
+ attr_reader :success, :workspace, :channel, :warnings, :error
5
+
6
+ def initialize(success:, workspace: nil, channel: nil, warnings: [], error: nil)
7
+ @success = success
8
+ @workspace = workspace
9
+ @channel = channel
10
+ @warnings = warnings
11
+ @error = error
12
+ end
13
+
14
+ def success?
15
+ @success
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ module ApiAlerts
2
+ VERSION = '1.0.0.alpha.1'.freeze
3
+ INTEGRATION_NAME = 'ruby'.freeze
4
+ API_URL = 'https://api.apialerts.com/event'.freeze
5
+ TIMEOUT_SECONDS = 30
6
+ end
data/lib/apialerts.rb ADDED
@@ -0,0 +1,46 @@
1
+ require_relative 'apialerts/version'
2
+ require_relative 'apialerts/event'
3
+ require_relative 'apialerts/result'
4
+ require_relative 'apialerts/client'
5
+
6
+ # Global singleton facade for the API Alerts client.
7
+ #
8
+ # ApiAlerts.configure('your-api-key')
9
+ # ApiAlerts.send(ApiAlerts::Event.new(message: 'Deploy complete'))
10
+ module ApiAlerts
11
+ class << self
12
+ # Initialise the global client. Subsequent calls are no-ops.
13
+ def configure(api_key, debug: false)
14
+ @client ||= Client.new(api_key, debug: debug)
15
+ end
16
+
17
+ # Override the integration name, version, and base URL on the global client.
18
+ # No-op if configure has not been called yet.
19
+ def set_overrides(integration, version, base_url)
20
+ @client&.set_overrides(integration, version, base_url)
21
+ end
22
+
23
+ # Send an event - fire-and-forget. Never raises.
24
+ # Silently does nothing if the global client has not been initialised.
25
+ def send(event, api_key: nil)
26
+ unless @client
27
+ warn 'x (apialerts.com) Error: client not configured'
28
+ return
29
+ end
30
+ @client.send(event, api_key: api_key)
31
+ end
32
+
33
+ # Send an event and return a SendResult. Never raises.
34
+ # Returns SendResult with success: false if not configured.
35
+ def send_async(event, api_key: nil)
36
+ return SendResult.new(success: false, error: 'client not configured') unless @client
37
+
38
+ @client.send_async(event, api_key: api_key)
39
+ end
40
+
41
+ # @private - for use in tests only
42
+ def reset!
43
+ @client = nil
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apialerts
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - API Alerts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-06-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.65'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.65'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.23'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.23'
69
+ description: Effortless project notifications. Send once, deliver everywhere.
70
+ email:
71
+ - support@apialerts.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - lib/apialerts.rb
79
+ - lib/apialerts/client.rb
80
+ - lib/apialerts/event.rb
81
+ - lib/apialerts/result.rb
82
+ - lib/apialerts/version.rb
83
+ homepage: https://apialerts.com
84
+ licenses:
85
+ - MIT
86
+ metadata:
87
+ source_code_uri: https://github.com/apialerts/apialerts-ruby
88
+ bug_tracker_uri: https://github.com/apialerts/apialerts-ruby/issues
89
+ rubygems_mfa_required: 'true'
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 3.0.0
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.5.22
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Ruby client for the API Alerts notification platform
109
+ test_files: []