├── .gitignore ├── lib ├── pagerduty │ ├── version.rb │ ├── http_transport.rb │ ├── events_api_v1.rb │ └── events_api_v2.rb └── pagerduty.rb ├── script └── setup.sh ├── Rakefile ├── Gemfile ├── spec ├── spec_helper.rb ├── support │ ├── warnings.rb │ └── http_client_exception.rb ├── pagerduty_spec.rb └── pagerduty │ ├── http_transport_spec.rb │ ├── events_api_v1_spec.rb │ └── events_api_v2_spec.rb ├── .github └── workflows │ └── test.yml ├── .rubocop.yml ├── LICENSE.txt ├── pagerduty.gemspec ├── README.md └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | rdoc 3 | *.log 4 | .DS_Store 5 | *.swp 6 | *.swo 7 | pkg 8 | .bundle 9 | /bin 10 | -------------------------------------------------------------------------------- /lib/pagerduty/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Pagerduty 4 | VERSION = "4.0.1" 5 | end 6 | -------------------------------------------------------------------------------- /script/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "This command will setup your local dev environment, including" 4 | echo " * bundle install" 5 | echo 6 | 7 | echo "Bundling..." 8 | bundle install --binstubs bin --path .bundle 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rspec/core/rake_task" 5 | require "rubocop/rake_task" 6 | 7 | task default: %i[spec rubocop] 8 | 9 | RSpec::Core::RakeTask.new(:spec) do |t| 10 | t.verbose = false 11 | t.ruby_opts = "-w" 12 | end 13 | 14 | RuboCop::RakeTask.new(:rubocop) 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | gemspec 5 | 6 | group :development, :test do 7 | gem "rake" 8 | gem "rspec-given", (Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6") ? "< 3.8.1" : "~> 3") 9 | gem "rubocop", (Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4") ? "~> 0" : "~> 1") 10 | end 11 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Dir[File.expand_path("support/**/*.rb", __dir__)].sort.each { |f| require f } 4 | 5 | Warnings.silenced do 6 | require "rspec/given" 7 | require "json" 8 | require "net/https" 9 | end 10 | 11 | require "pagerduty" 12 | 13 | RSpec.configure do |config| 14 | config.color = true 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/warnings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Warning[:deprecated] = true if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.2") 4 | 5 | module Warnings 6 | def self.silenced(&block) 7 | with_flag(nil, &block) 8 | end 9 | 10 | def self.with_flag(flag) 11 | old_verbose = $VERBOSE 12 | $VERBOSE = flag 13 | yield 14 | ensure 15 | $VERBOSE = old_verbose 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/http_client_exception.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "net/https" 4 | 5 | # Net::HTTPClientException was introduced in Ruby 2.6 as a better named 6 | # replacement for Net::HTTPServerException 7 | # 8 | # https://bugs.ruby-lang.org/issues/14688 9 | # 10 | # This allows use of the new class name while running the test suite on Rubies 11 | # older than version 2.6. 12 | Net::HTTPClientException = Net::HTTPServerException unless defined?(Net::HTTPClientException) 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | name: Test (Ruby ${{ matrix.ruby }}) 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | ruby: ['3.3', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5', '2.4', '2.3'] 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Set up Ruby ${{ matrix.ruby }} 13 | uses: ruby/setup-ruby@v1 14 | with: 15 | ruby-version: ${{ matrix.ruby }} 16 | bundler-cache: true 17 | - name: RSpec 18 | run: bundle exec rake spec --trace 19 | lint: 20 | name: Lint 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: ruby/setup-ruby@v1 25 | with: 26 | ruby-version: '3.3' 27 | bundler-cache: true 28 | - name: Rubocop 29 | run: bundle exec rake rubocop --trace 30 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | DisplayCopNames: true 3 | DisplayStyleGuide: true 4 | NewCops: disable 5 | SuggestExtensions: false 6 | TargetRubyVersion: 2.3 7 | Exclude: 8 | - .bundle/**/* 9 | - bin/**/* 10 | - vendor/**/* 11 | 12 | Layout/HashAlignment: 13 | EnforcedHashRocketStyle: table 14 | EnforcedColonStyle: table 15 | 16 | Metrics/BlockLength: 17 | Exclude: 18 | - spec/**/* 19 | 20 | Style/BlockDelimiters: 21 | Exclude: 22 | - spec/**/* 23 | 24 | Style/Documentation: 25 | Enabled: false 26 | 27 | Style/GuardClause: 28 | Enabled: false 29 | 30 | Style/SpecialGlobalVars: 31 | Enabled: false 32 | 33 | Style/StringLiterals: 34 | EnforcedStyle: double_quotes 35 | 36 | Style/TrailingCommaInArguments: 37 | EnforcedStyleForMultiline: comma 38 | 39 | Style/TrailingCommaInArrayLiteral: 40 | EnforcedStyleForMultiline: comma 41 | 42 | Style/TrailingCommaInHashLiteral: 43 | EnforcedStyleForMultiline: comma 44 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Envato, Hailey Somerville & Orien Madgwick 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /pagerduty.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path("lib", __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require "pagerduty/version" 6 | 7 | Gem::Specification.new do |gem| 8 | gem.name = "pagerduty" 9 | gem.version = Pagerduty::VERSION 10 | gem.authors = ["Hailey Somerville", "Orien Madgwick"] 11 | gem.email = ["hailey@hailey.lol", "_@orien.io"] 12 | gem.description = 13 | "Provides a lightweight interface for calling the PagerDuty Events API" 14 | gem.summary = "PagerDuty Events API client library" 15 | gem.homepage = "https://github.com/envato/pagerduty" 16 | gem.license = "MIT" 17 | 18 | gem.metadata = { 19 | "bug_tracker_uri" => "#{gem.homepage}/issues", 20 | "changelog_uri" => "#{gem.homepage}/blob/v#{gem.version}/CHANGELOG.md", 21 | "documentation_uri" => "https://www.rubydoc.info/gems/#{gem.name}/#{gem.version}", 22 | "homepage_uri" => gem.homepage, 23 | "source_code_uri" => "#{gem.homepage}/tree/v#{gem.version}", 24 | } 25 | 26 | gem.require_paths = ["lib"] 27 | gem.files = `git ls-files -z`.split("\x0").select do |f| 28 | f.match(%r{^(?:README|LICENSE|CHANGELOG|lib/)}) 29 | end 30 | gem.required_ruby_version = ">= 2.3" 31 | end 32 | -------------------------------------------------------------------------------- /lib/pagerduty/http_transport.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "json" 4 | require "net/https" 5 | 6 | module Pagerduty 7 | # @private 8 | class HttpTransport 9 | HOST = "events.pagerduty.com" 10 | PORT = 443 11 | private_constant :HOST, :PORT 12 | 13 | def initialize(config) 14 | @path = config.fetch(:path) 15 | @proxy = config[:proxy] || {} 16 | end 17 | 18 | def send_payload(payload) 19 | response = post(payload.to_json) 20 | response.error! unless transported?(response) 21 | JSON.parse(response.body) 22 | end 23 | 24 | private 25 | 26 | def post(payload) 27 | post = Net::HTTP::Post.new(@path) 28 | post.body = payload 29 | http.request(post) 30 | end 31 | 32 | def http 33 | http = http_proxy.new(HOST, PORT) 34 | http.use_ssl = true 35 | http.verify_mode = OpenSSL::SSL::VERIFY_PEER 36 | http.open_timeout = 60 37 | http.read_timeout = 60 38 | http 39 | end 40 | 41 | def http_proxy 42 | Net::HTTP.Proxy( 43 | @proxy[:host], 44 | @proxy[:port], 45 | @proxy[:username], 46 | @proxy[:password], 47 | ) 48 | end 49 | 50 | def transported?(response) 51 | response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/pagerduty_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "spec_helper" 4 | 5 | RSpec.describe Pagerduty do 6 | describe ".build" do 7 | When(:build) { Pagerduty.build(config) } 8 | 9 | context "given an integration key and API version: `1`" do 10 | Given(:config) { { integration_key: "test-key", api_version: 1 } } 11 | Then { expect(build).to be_a(Pagerduty::EventsApiV1) } 12 | end 13 | 14 | context "given an integration key and API version: `'1'`" do 15 | Given(:config) { { integration_key: "test-key", api_version: "1" } } 16 | Then { expect(build).to be_a(Pagerduty::EventsApiV1) } 17 | end 18 | 19 | context "given an integration key, but no API version" do 20 | Given(:config) { { integration_key: "test-key" } } 21 | Then { 22 | expect(build).to have_raised(ArgumentError, 23 | "api_version not provided") 24 | } 25 | end 26 | 27 | context "given an API version, but no integration key" do 28 | Given(:config) { { api_version: 1 } } 29 | Then { 30 | expect(build).to have_raised(ArgumentError, 31 | "integration_key not provided") 32 | } 33 | end 34 | 35 | context "given an integration key and an unknown API version" do 36 | Given(:config) { { integration_key: "test-key", api_version: 0 } } 37 | Then { 38 | expect(build).to have_raised(ArgumentError, 39 | "api_version 0 not supported") 40 | } 41 | end 42 | 43 | context "given an incident key" do 44 | Given(:config) { 45 | { incident_key: "ik", integration_key: "test-key", api_version: 1 } 46 | } 47 | Then { 48 | expect(build).to have_raised(ArgumentError, "incident_key provided") 49 | } 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/pagerduty.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "pagerduty/version" 4 | require "pagerduty/http_transport" 5 | require "pagerduty/events_api_v1" 6 | require "pagerduty/events_api_v2" 7 | 8 | class PagerdutyException < StandardError 9 | attr_reader :pagerduty_instance, :api_response 10 | 11 | def initialize(instance, response, message) 12 | super(message) 13 | @pagerduty_instance = instance 14 | @api_response = response 15 | end 16 | end 17 | 18 | module Pagerduty 19 | # Build an instance that will send API calls to the specified Pagerduty 20 | # Events API version. 21 | # 22 | # @example Build an instance for the Events API version 1 23 | # pagerduty = Pagerduty.build( 24 | # integration_key: "", 25 | # api_version: 1, 26 | # ) 27 | # 28 | # @example Build an instance using an HTTP proxy for API requests 29 | # pagerduty = Pagerduty.build( 30 | # integration_key: "", 31 | # api_version: 1, 32 | # http_proxy: { 33 | # host: "my.http.proxy.local", 34 | # port: 3128, 35 | # username: "", 36 | # password: "", 37 | # } 38 | # ) 39 | # 40 | # @option config [String] integration_key Authentication key for connecting 41 | # to PagerDuty. A UUID expressed as a 32-digit hexadecimal number. 42 | # Integration keys are generated by creating a new service, or creating a 43 | # new integration for an existing service in PagerDuty, and can be found on 44 | # a service's Integrations tab. This option is required. 45 | # 46 | # @option config [String] api_version The version of the Pagerduty events API. 47 | # The gem currently supports version 1 (`1`). This option is required. 48 | # 49 | # @option config [String] http_proxy.host The DNS name or IP address of the 50 | # proxy host. If nil or unprovided an HTTP proxy will not be used. 51 | # 52 | # @option config [String] http_proxy.port The TCP port to use to access the 53 | # proxy. 54 | # 55 | # @option config [String] http_proxy.username username if authorization is 56 | # required to use the proxy. 57 | # 58 | # @option config [String] http_proxy.password password if authorization is 59 | # required to use the proxy. 60 | # 61 | # @return [Pagerduty::EventsApiV1] the built instance. 62 | # 63 | # @raise [ArgumentError] If integration_key or api_version options are not 64 | # provided. Or if the provided api_version is unsupported. 65 | # 66 | def self.build(config) 67 | raise ArgumentError, "integration_key not provided" unless config.key?(:integration_key) 68 | raise ArgumentError, "incident_key provided" if config.key?(:incident_key) 69 | 70 | version = config.fetch(:api_version) do 71 | raise ArgumentError, "api_version not provided" 72 | end 73 | events_api_class(version).new(config) 74 | end 75 | 76 | def self.events_api_class(version) 77 | class_name = "Pagerduty::EventsApiV#{version}" 78 | if const_defined?(class_name) 79 | const_get(class_name) 80 | else 81 | raise ArgumentError, "api_version #{version.inspect} not supported" 82 | end 83 | end 84 | private_class_method :events_api_class 85 | end 86 | -------------------------------------------------------------------------------- /spec/pagerduty/http_transport_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "spec_helper" 4 | 5 | RSpec.describe Pagerduty::HttpTransport do 6 | Given(:http_transport) { Pagerduty::HttpTransport.new(options) } 7 | 8 | Given(:options) { { path: "/path/provided/" } } 9 | Given(:http) { spy } 10 | Given(:http_proxy) { spy(new: http) } 11 | Given { allow(http).to receive(:request).and_return(standard_response) } 12 | Given { allow(Net::HTTP).to receive(:Proxy).and_return(http_proxy) } 13 | Given(:post) { spy } 14 | Given { allow(Net::HTTP::Post).to receive(:new).and_return(post) } 15 | 16 | describe "::send_payload" do 17 | Given(:payload) { 18 | { 19 | event_type: "trigger", 20 | service_key: "test-srvc-key", 21 | description: "test-desc", 22 | details: { key: "value" }, 23 | } 24 | } 25 | 26 | When(:response) { http_transport.send_payload(payload) } 27 | 28 | describe "provides the correct request" do 29 | Then { 30 | expect(post).to have_received(:body=).with( 31 | '{"event_type":"trigger",'\ 32 | '"service_key":"test-srvc-key",'\ 33 | '"description":"test-desc",'\ 34 | '"details":{"key":"value"}}', 35 | ) 36 | } 37 | end 38 | 39 | describe "handles all responses" do 40 | context "PagerDuty successfully creates the incident" do 41 | Given { 42 | allow(http) 43 | .to receive(:request) 44 | .and_return(response_with_body(<<-JSON)) 45 | { 46 | "status": "success", 47 | "incident_key": "My Incident Key", 48 | "message": "Event processed" 49 | } 50 | JSON 51 | } 52 | 53 | Then { expect(response).to include("status" => "success") } 54 | Then { 55 | expect(response).to include("incident_key" => "My Incident Key") 56 | } 57 | end 58 | 59 | context "PagerDuty fails to create the incident" do 60 | Given { 61 | allow(http) 62 | .to receive(:request) 63 | .and_return(response_with_body(<<-JSON)) 64 | { 65 | "status": "failure", 66 | "message": "Event not processed" 67 | } 68 | JSON 69 | } 70 | Then { expect(response).to include("status" => "failure") } 71 | Then { expect(response).to_not include("incident_key") } 72 | end 73 | 74 | context "PagerDuty responds with HTTP bad request" do 75 | Given { allow(http).to receive(:request).and_return(bad_request) } 76 | Then { expect(response).to have_raised Net::HTTPClientException } 77 | end 78 | end 79 | 80 | describe "HTTPS use" do 81 | Then { expect(http).to have_received(:use_ssl=).with(true) } 82 | Then { expect(http).to_not have_received(:ca_path=) } 83 | Then { expect(http).to_not have_received(:verify_depth=) } 84 | Then { 85 | expect(http) 86 | .to have_received(:verify_mode=) 87 | .with(OpenSSL::SSL::VERIFY_PEER) 88 | } 89 | end 90 | 91 | describe "proxy use" do 92 | Given(:options) { 93 | { 94 | path: "/path/provided", 95 | proxy: { 96 | host: "test-proxy-host", 97 | port: "test-proxy-port", 98 | username: "test-proxy-username", 99 | password: "test-proxy-password", 100 | }, 101 | } 102 | } 103 | Then { 104 | expect(Net::HTTP) 105 | .to have_received(:Proxy) 106 | .with( 107 | "test-proxy-host", 108 | "test-proxy-port", 109 | "test-proxy-username", 110 | "test-proxy-password", 111 | ) 112 | } 113 | context "given nil proxy config" do 114 | Given(:options) { { path: "/path/provided", proxy: nil } } 115 | Then { 116 | expect(Net::HTTP).to have_received(:Proxy).with(nil, nil, nil, nil) 117 | } 118 | end 119 | end 120 | 121 | describe "timeouts" do 122 | Then { expect(http).to have_received(:open_timeout=).with(60) } 123 | Then { expect(http).to have_received(:read_timeout=).with(60) } 124 | end 125 | end 126 | 127 | def standard_response 128 | response_with_body( 129 | '{ "status": "success", "incident_key": "My Incident Key" }', 130 | ) 131 | end 132 | 133 | def response_with_body(body) 134 | Net::HTTPSuccess.new(1.1, "200", "OK").tap do |response| 135 | allow(response).to receive(:body).and_return(body) 136 | end 137 | end 138 | 139 | def bad_request 140 | Net::HTTPBadRequest.new(1.1, "400", "Bad Request") 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pagerduty 2 | 3 | [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/pagerduty/blob/HEAD/LICENSE.txt) 4 | [![Gem Version](https://img.shields.io/gem/v/pagerduty.svg?maxAge=2592000)](https://rubygems.org/gems/pagerduty) 5 | [![Gem Downloads](https://img.shields.io/gem/dt/pagerduty.svg?maxAge=2592000)](https://rubygems.org/gems/pagerduty) 6 | [![Build Status](https://github.com/envato/pagerduty/workflows/build/badge.svg?branch=main)](https://github.com/envato/pagerduty/actions?query=workflow%3Abuild+branch%3Amain) 7 | 8 | Provides a lightweight Ruby interface for calling the [PagerDuty Events 9 | API][events-v2-docs]. 10 | 11 | [events-v2-docs]: https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview 12 | 13 | ## Installation 14 | 15 | Add this line to your application's Gemfile: 16 | 17 | ```ruby 18 | gem 'pagerduty' 19 | ``` 20 | 21 | And then execute: 22 | 23 | $ bundle 24 | 25 | Or install it yourself as: 26 | 27 | $ gem install pagerduty 28 | 29 | ## Usage 30 | 31 | First, obtain an Events API integration key from PagerDuty. Follow the 32 | [instructions][integration-key-documentation] in PagerDuty's documentation to 33 | procure one. 34 | 35 | [integration-key-documentation]: https://support.pagerduty.com/docs/services-and-integrations#create-a-generic-events-api-integration 36 | 37 | 38 | ### Events API V2 39 | 40 | ```ruby 41 | # Instantiate a Pagerduty service object providing an integration key and the 42 | # desired API version: 2 43 | pagerduty = Pagerduty.build( 44 | integration_key: "", 45 | api_version: 2 46 | ) 47 | 48 | # Trigger an incident providing minimal details 49 | incident = pagerduty.trigger( 50 | summary: "summary", 51 | source: "source", 52 | severity: "critical" 53 | ) 54 | 55 | # Trigger an incident providing full context 56 | incident = pagerduty.trigger( 57 | summary: "Example alert on host1.example.com", 58 | source: "monitoringtool:cloudvendor:central-region-dc-01:852559987:cluster/api-stats-prod-003", 59 | severity: %w[critical error warning info].sample, 60 | timestamp: Time.now, 61 | component: "postgres", 62 | group: "prod-datapipe", 63 | class: "deploy", 64 | custom_details: { 65 | ping_time: "1500ms", 66 | load_avg: 0.75 67 | }, 68 | images: [ 69 | { 70 | src: "https://www.pagerduty.com/wp-content/uploads/2016/05/pagerduty-logo-green.png", 71 | href: "https://example.com/", 72 | alt: "Example text", 73 | }, 74 | ], 75 | links: [ 76 | { 77 | href: "https://example.com/", 78 | text: "Link text", 79 | }, 80 | ], 81 | client: "Sample Monitoring Service", 82 | client_url: "https://monitoring.example.com" 83 | ) 84 | 85 | # Acknowledge and/or resolve the incident 86 | incident.acknowledge 87 | incident.resolve 88 | 89 | # Provide a client-defined incident key 90 | # (this can be used to update existing incidents) 91 | incident = pagerduty.incident("") 92 | incident.trigger( 93 | summary: "summary", 94 | source: "source", 95 | severity: "critical" 96 | ) 97 | incident.acknowledge 98 | incident.resolve 99 | ``` 100 | 101 | See the [PagerDuty Events API V2 documentation][events-v2-docs] for a 102 | detailed description on the parameters you can send when triggering an 103 | incident. 104 | 105 | ### Events API V1 106 | 107 | The following code snippet shows how to use the [Pagerduty Events API version 108 | 1](https://v2.developer.pagerduty.com/docs/events-api). 109 | 110 | ```ruby 111 | # Instantiate a Pagerduty with a service integration key 112 | pagerduty = Pagerduty.build( 113 | integration_key: "", 114 | api_version: 1, 115 | ) 116 | 117 | # Trigger an incident 118 | incident = pagerduty.trigger( 119 | "FAILURE for production/HTTP on machine srv01.acme.com", 120 | ) 121 | 122 | # Trigger an incident providing context and details 123 | incident = pagerduty.trigger( 124 | "FAILURE for production/HTTP on machine srv01.acme.com", 125 | client: "Sample Monitoring Service", 126 | client_url: "https://monitoring.service.com", 127 | contexts: [ 128 | { 129 | type: "link", 130 | href: "http://acme.pagerduty.com", 131 | text: "View the incident on PagerDuty", 132 | }, 133 | { 134 | type: "image", 135 | src: "https://chart.googleapis.com/chart?chs=600x400&chd=t:6,2,9,5,2,5,7,4,8,2,1&cht=lc&chds=a&chxt=y&chm=D,0033FF,0,0,5,1", 136 | } 137 | ], 138 | details: { 139 | ping_time: "1500ms", 140 | load_avg: 0.75, 141 | }, 142 | ) 143 | 144 | # Acknowledge the incident 145 | incident.acknowledge 146 | 147 | # Acknowledge, providing a description and extra details 148 | incident.acknowledge( 149 | "Engineers are investigating the incident", 150 | { 151 | ping_time: "1700ms", 152 | load_avg: 0.71, 153 | } 154 | ) 155 | 156 | # Resolve the incident 157 | incident.resolve 158 | 159 | # Resolve, providing a description and extra details 160 | incident.acknowledge( 161 | "A fix has been deployed and the service has recovered", 162 | { 163 | ping_time: "120ms", 164 | load_avg: 0.23, 165 | } 166 | ) 167 | 168 | # Provide a client defined incident key 169 | # (this can be used to update existing incidents) 170 | incident = pagerduty.incident("") 171 | incident.trigger("Description of the event") 172 | incident.acknowledge 173 | incident.resolve 174 | ``` 175 | 176 | See the [PagerDuty Events API V1 177 | documentation](https://v2.developer.pagerduty.com/docs/trigger-events) for a 178 | detailed description of the parameters you can send when triggering an 179 | incident. 180 | 181 | ### HTTP Proxy Support 182 | 183 | One can explicitly define an HTTP proxy like this: 184 | 185 | ```ruby 186 | pagerduty = Pagerduty.build( 187 | integration_key: "", 188 | api_version: 2, # The HTTP proxy settings work with either API version 189 | http_proxy: { 190 | host: "my.http.proxy.local", 191 | port: 3128, 192 | username: "", 193 | password: "", 194 | } 195 | ) 196 | 197 | # Subsequent API calls will then be sent via the HTTP proxy 198 | incident = pagerduty.trigger( 199 | summary: "summary", 200 | source: "source", 201 | severity: "critical" 202 | ) 203 | ``` 204 | 205 | ### Debugging Error Responses 206 | 207 | The gem doesn't encapsulate HTTP error responses from PagerDuty. Here's how to 208 | go about debugging these unhappy cases: 209 | 210 | ```ruby 211 | begin 212 | pagerduty.trigger( 213 | summary: "summary", 214 | source: "source", 215 | severity: "critical" 216 | ) 217 | rescue Net::HTTPClientException => error 218 | error.response.code #=> "400" 219 | error.response.message #=> "Bad Request" 220 | error.response.body #=> "{\"status\":\"invalid event\",\"message\":\"Event object is invalid\",\"errors\":[\"Service key is the wrong length (should be 32 characters)\"]}" 221 | end 222 | ``` 223 | 224 | ## Contributing 225 | 226 | 1. Fork it ( https://github.com/envato/pagerduty/fork ) 227 | 2. Create your feature branch (`git checkout -b my-new-feature`) 228 | 3. Commit your changes (`git commit -am 'Add some feature'`) 229 | 4. Push to the branch (`git push origin my-new-feature`) 230 | 5. Create a new Pull Request 231 | -------------------------------------------------------------------------------- /lib/pagerduty/events_api_v1.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Pagerduty 4 | # Trigger incidents via the PagerDuty Events API version 1. 5 | # 6 | # @see https://v2.developer.pagerduty.com/docs/events-api PagerDuty Events 7 | # API V1 documentation 8 | # 9 | # @see Pagerduty.build 10 | # 11 | # @see Pagerduty::EventsApiV1::Incident 12 | # 13 | class EventsApiV1 14 | # Rather than using this directly, use the {Pagerduty.build} method to 15 | # construct an instance. 16 | # 17 | # @option config [String] integration_key Authentication key for connecting 18 | # to PagerDuty. A UUID expressed as a 32-digit hexadecimal number. 19 | # Integration keys are generated by creating a new service, or creating a 20 | # new integration for an existing service in PagerDuty, and can be found 21 | # on a service's Integrations tab. This option is required. 22 | # 23 | # @option config [String] http_proxy.host The DNS name or IP address of the 24 | # proxy host. If nil or unprovided an HTTP proxy will not be used. 25 | # 26 | # @option config [String] http_proxy.port The TCP port to use to access the 27 | # proxy. 28 | # 29 | # @option config [String] http_proxy.username username if authorization is 30 | # required to use the proxy. 31 | # 32 | # @option config [String] http_proxy.password password if authorization is 33 | # required to use the proxy. 34 | # 35 | # @see Pagerduty.build 36 | # 37 | def initialize(config) 38 | @config = config 39 | end 40 | 41 | # Send PagerDuty a trigger event to report a new or ongoing problem. 42 | # 43 | # @example Trigger an incident 44 | # incident = pagerduty.trigger( 45 | # "" 46 | # ) 47 | # 48 | # @example Trigger an incident, providing more context and details 49 | # incident = pagerduty.trigger( 50 | # "FAILURE for production/HTTP on machine srv01.acme.com", 51 | # client: "Sample Monitoring Service", 52 | # client_url: "https://monitoring.service.com", 53 | # contexts: [ 54 | # { 55 | # type: "link", 56 | # href: "http://acme.pagerduty.com", 57 | # text: "View the incident on PagerDuty", 58 | # }, 59 | # { 60 | # type: "image", 61 | # src: "https://chart.googleapis.com/chart.png", 62 | # } 63 | # ], 64 | # details: { 65 | # ping_time: "1500ms", 66 | # load_avg: 0.75, 67 | # }, 68 | # ) 69 | # 70 | # @param [String] description A short description of the problem that led to 71 | # this trigger. This field (or a truncated version) will be used when 72 | # generating phone calls, SMS messages and alert emails. It will also 73 | # appear on the incidents tables in the PagerDuty UI. The maximum length 74 | # is 1024 characters. 75 | # 76 | # @option options [String] client The name of the monitoring client that is 77 | # triggering this event. 78 | # 79 | # @option options [String] client_url The URL of the monitoring client that 80 | # is triggering this event. 81 | # 82 | # @option options [Array] contexts An array of objects. Contexts to be 83 | # included with the incident trigger such as links to graphs or images. 84 | # 85 | # @option options [Hash] details An arbitrary hash containing any data you'd 86 | # like included in the incident log. 87 | # 88 | # @return [Pagerduty::EventsApiV1::Incident] The triggered incident. 89 | # 90 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 91 | # not "success" 92 | # 93 | def trigger(description, options = {}) 94 | config = @config.merge(incident_key: options[:incident_key]) 95 | options = options.reject { |key| key == :incident_key } 96 | Incident.new(config).trigger(description, options) 97 | end 98 | 99 | # @param [String] incident_key Identifies the incident to which 100 | # this trigger event should be applied. If there's no open (i.e. 101 | # unresolved) incident with this key, a new one will be created. If 102 | # there's already an open incident with a matching key, this event will be 103 | # appended to that incident's log. The event key provides an easy way to 104 | # "de-dup" problem reports. If this field isn't provided, PagerDuty will 105 | # automatically open a new incident with a unique key. The maximum length 106 | # is 255 characters. 107 | # 108 | # @return [Pagerduty::EventsApiV1::Incident] The incident referenced by the 109 | # key. 110 | # 111 | # @raise [ArgumentError] If incident_key is nil 112 | # 113 | def incident(incident_key) 114 | raise ArgumentError, "incident_key is nil" if incident_key.nil? 115 | 116 | Incident.new(@config.merge(incident_key: incident_key)) 117 | end 118 | 119 | class Incident 120 | attr_reader :incident_key 121 | 122 | # @option (see Pagerduty::EventsApiV1#initialize) 123 | # 124 | # @option config [String] incident_key Identifies the incident to which 125 | # this trigger event should be applied. If there's no open 126 | # (i.e. unresolved) incident with this key, a new one will be created. 127 | # If there's already an open incident with a matching key, this event 128 | # will be appended to that incident's log. The event key provides an 129 | # easy way to "de-dup" problem reports. If this field isn't provided, 130 | # PagerDuty will automatically open a new incident with a unique key. 131 | # The maximum length is 255 characters. 132 | # 133 | def initialize(config) 134 | @integration_key = config.fetch(:integration_key) do 135 | raise ArgumentError "integration_key not provided" 136 | end 137 | @incident_key = config[:incident_key] 138 | @transport = Pagerduty::HttpTransport.new( 139 | path: "/generic/2010-04-15/create_event.json", 140 | proxy: config[:http_proxy], 141 | ) 142 | end 143 | 144 | # Send PagerDuty a trigger event to report a new or ongoing problem. When 145 | # PagerDuty receives a trigger event, it will either open a new incident, 146 | # or add a new trigger log entry to an existing incident, depending on the 147 | # provided incident_key. 148 | # 149 | # @example Trigger or update an incident 150 | # incident.trigger( 151 | # "" 152 | # ) 153 | # 154 | # @example Trigger or update an incident, providing more context 155 | # incident.trigger( 156 | # "FAILURE for production/HTTP on machine srv01.acme.com", 157 | # client: "Sample Monitoring Service", 158 | # client_url: "https://monitoring.service.com", 159 | # contexts: [ 160 | # { 161 | # type: "link", 162 | # href: "http://acme.pagerduty.com", 163 | # text: "View the incident on PagerDuty", 164 | # }, 165 | # { 166 | # type: "image", 167 | # src: "https://chart.googleapis.com/chart.png", 168 | # } 169 | # ], 170 | # details: { 171 | # ping_time: "1500ms", 172 | # load_avg: 0.75, 173 | # }, 174 | # ) 175 | # 176 | # @param (see Pagerduty::EventsApiV1#trigger) 177 | # @option (see Pagerduty::EventsApiV1#trigger) 178 | def trigger(description, options = {}) 179 | raise ArgumentError, "incident_key provided" if options.key?(:incident_key) 180 | 181 | options = options.merge(description: description) 182 | options[:incident_key] = @incident_key unless @incident_key.nil? 183 | response = api_call("trigger", options) 184 | @incident_key = response["incident_key"] 185 | self 186 | end 187 | 188 | # Acknowledge the referenced incident. While an incident is acknowledged, 189 | # it won't generate any additional notifications, even if it receives new 190 | # trigger events. Send PagerDuty an acknowledge event when you know 191 | # someone is presently working on the problem. 192 | # 193 | # @example Acknowledge the incident 194 | # incident.acknowledge 195 | # 196 | # @example Acknowledge, providing a description and extra details 197 | # incident.acknowledge( 198 | # "Engineers are investigating the incident", 199 | # { 200 | # ping_time: "1700ms", 201 | # load_avg: 0.71, 202 | # } 203 | # ) 204 | # 205 | # @param [String] description Text that will appear in the incident's log 206 | # associated with this event. 207 | # 208 | # @param [Hash] details An arbitrary hash containing any data you'd like 209 | # included in the incident log. 210 | # 211 | # @return [Pagerduty::EventsApiV1::Incident] self 212 | # 213 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 214 | # not "success" 215 | # 216 | def acknowledge(description = nil, details = nil) 217 | modify_incident("acknowledge", description, details) 218 | end 219 | 220 | # Resolve the referenced incident. Once an incident is resolved, it won't 221 | # generate any additional notifications. New trigger events with the same 222 | # incident_key as a resolved incident won't re-open the incident. Instead, 223 | # a new incident will be created. Send PagerDuty a resolve event when the 224 | # problem that caused the initial trigger event has been fixed. 225 | # 226 | # @example Resolve the incident 227 | # incident.resolve 228 | # 229 | # @example Resolve, providing a description and extra details 230 | # incident.resolve( 231 | # "A fix has been deployed and the service has recovered", 232 | # { 233 | # ping_time: "130ms", 234 | # load_avg: 0.23, 235 | # } 236 | # ) 237 | # 238 | # @param [String] description Text that will appear in the incident's log 239 | # associated with this event. 240 | # 241 | # @param [Hash] details An arbitrary hash containing any data you'd like 242 | # included in the incident log. 243 | # 244 | # @return [Pagerduty::EventsApiV1::Incident] self 245 | # 246 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 247 | # not "success" 248 | # 249 | def resolve(description = nil, details = nil) 250 | modify_incident("resolve", description, details) 251 | end 252 | 253 | private 254 | 255 | def modify_incident(event_type, description, details) 256 | options = { incident_key: incident_key } 257 | options[:description] = description if description 258 | options[:details] = details if details 259 | api_call(event_type, options) 260 | self 261 | end 262 | 263 | def api_call(event_type, args) 264 | args = args.merge( 265 | service_key: @integration_key, 266 | event_type: event_type, 267 | ) 268 | response = @transport.send_payload(args) 269 | raise PagerdutyException.new(self, response, response["message"]) unless response["status"] == "success" 270 | 271 | response 272 | end 273 | end 274 | end 275 | end 276 | -------------------------------------------------------------------------------- /lib/pagerduty/events_api_v2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "time" 4 | 5 | module Pagerduty 6 | # Trigger incidents via the PagerDuty Events API version 2. 7 | # 8 | # @see https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview 9 | # PagerDuty Events API V2 documentation 10 | # 11 | # @see Pagerduty.build 12 | # 13 | # @see Pagerduty::EventsApiV2::Incident 14 | # 15 | class EventsApiV2 16 | # Rather than using this directly, use the {Pagerduty.build} method to 17 | # construct an instance. 18 | # 19 | # @option config [String] integration_key Authentication key for connecting 20 | # to PagerDuty. A UUID expressed as a 32-digit hexadecimal number. 21 | # Integration keys are generated by creating a new service, or creating a 22 | # new integration for an existing service in PagerDuty, and can be found 23 | # on a service's Integrations tab. This option is required. 24 | # 25 | # @option config [String] http_proxy.host The DNS name or IP address of the 26 | # proxy host. If nil or unprovided an HTTP proxy will not be used. 27 | # 28 | # @option config [String] http_proxy.port The TCP port to use to access the 29 | # proxy. 30 | # 31 | # @option config [String] http_proxy.username username if authorization is 32 | # required to use the proxy. 33 | # 34 | # @option config [String] http_proxy.password password if authorization is 35 | # required to use the proxy. 36 | # 37 | # @see Pagerduty.build 38 | # 39 | def initialize(config = {}) 40 | @config = config 41 | end 42 | 43 | # Send PagerDuty a trigger event to report a new or ongoing problem. When 44 | # PagerDuty receives a trigger event, it will either open a new incident, or 45 | # add a new trigger log entry to an existing incident, depending on the 46 | # incident key. 47 | # 48 | # @example Trigger an incident, providing only required details 49 | # incident = pagerduty.trigger( 50 | # summary: "summary", 51 | # source: "source", 52 | # severity: "critical" 53 | # ) 54 | # 55 | # @example Trigger an incident providing full context 56 | # incident = pagerduty.trigger( 57 | # summary: "Example alert on host1.example.com", 58 | # source: "monitoringtool:host1.example.com/prod-003", 59 | # severity: %w[critical error warning info].sample, 60 | # timestamp: Time.now, 61 | # component: "postgres", 62 | # group: "prod-datapipe", 63 | # class: "deploy", 64 | # custom_details: { 65 | # ping_time: "1500ms", 66 | # load_avg: 0.75 67 | # }, 68 | # images: [ 69 | # { 70 | # src: "https://chart.googleapis.com/chart.png", 71 | # href: "https://example.com/", 72 | # alt: "Example text", 73 | # }, 74 | # ], 75 | # links: [ 76 | # { 77 | # href: "https://example.com/", 78 | # text: "Link text", 79 | # }, 80 | # ], 81 | # client: "Sample Monitoring Service", 82 | # client_url: "https://monitoring.example.com" 83 | # ) 84 | # 85 | # @option details [String] summary A brief text summary of the event, 86 | # used to generate the summaries/titles of any associated alerts. 87 | # The maximum permitted length of this property is 1024 characters. 88 | # 89 | # @option details [String] source The unique location of the affected 90 | # system, preferably a hostname or FQDN. 91 | # 92 | # @option details [String] severity The perceived severity of the status 93 | # the event is describing with respect to the affected system. This can 94 | # be "critical", "error", "warning" or "info". 95 | # 96 | # @option details [Time] timestamp The time at which the emitting tool 97 | # detected or generated the event. 98 | # 99 | # @option details [String] component Component of the source machine 100 | # that is responsible for the event, for example "mysql" or "eth0". 101 | # 102 | # @option details [String] group Logical grouping of components of a 103 | # service, for example "app-stack". 104 | # 105 | # @option details [String] class The class/type of the event, for 106 | # example "ping failure" or "cpu load". 107 | # 108 | # @option details [Hash] custom_details Additional details about the 109 | # event and affected system 110 | # 111 | # @option details [Array] images List of images to include. 112 | # 113 | # @option details [Array] links List of links to include. 114 | # 115 | # @return [Pagerduty::EventsApiV2::Incident] The triggered incident. 116 | # 117 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 118 | # not "success" 119 | # 120 | # @raise [ArgumentError] If details hash is nil 121 | # 122 | def trigger(details) 123 | Incident.new(@config).trigger(details) 124 | end 125 | 126 | # @param [String] incident_key The unique identifier for the incident. 127 | # 128 | # @return [Pagerduty::EventsApiV2::Incident] The incident referenced by the 129 | # provided key. 130 | # 131 | # @raise [ArgumentError] If incident_key is nil 132 | # 133 | def incident(incident_key) 134 | raise ArgumentError, "incident_key is nil" if incident_key.nil? 135 | 136 | Incident.new(@config.merge(incident_key: incident_key)) 137 | end 138 | 139 | class Incident 140 | attr_reader :incident_key 141 | 142 | # @option (see Pagerduty::EventsApiV1#initialize) 143 | # 144 | # @option config [String] incident_key Identifies the incident to which 145 | # this trigger event should be applied. If there's no open 146 | # (i.e. unresolved) incident with this key, a new one will be created. 147 | # If there's already an open incident with a matching key, this event 148 | # will be appended to that incident's log. The event key provides an 149 | # easy way to "de-dup" problem reports. If this field isn't provided, 150 | # PagerDuty will automatically open a new incident with a unique key. 151 | # The maximum length is 255 characters. 152 | # 153 | def initialize(config = {}) 154 | @integration_key = config.fetch(:integration_key) do 155 | raise ArgumentError "integration_key not provided" 156 | end 157 | @incident_key = config[:incident_key] 158 | @transport = Pagerduty::HttpTransport.new( 159 | path: "/v2/enqueue", 160 | proxy: config[:http_proxy], 161 | ) 162 | end 163 | 164 | # Send PagerDuty a trigger event to report a new or ongoing problem. When 165 | # PagerDuty receives a trigger event, it will either open a new incident, 166 | # or add a new trigger log entry to an existing incident, depending on 167 | # the incident key. 168 | # 169 | # @example Trigger an incident, providing only required details 170 | # incident = pagerduty.trigger( 171 | # summary: "summary", 172 | # source: "source", 173 | # severity: "critical" 174 | # ) 175 | # 176 | # @example Trigger an incident providing full context 177 | # incident = pagerduty.trigger( 178 | # summary: "Example alert on host1.example.com", 179 | # source: "monitoringtool:host1.example.com/prod-003", 180 | # severity: %w[critical error warning info].sample, 181 | # timestamp: Time.now, 182 | # component: "postgres", 183 | # group: "prod-datapipe", 184 | # class: "deploy", 185 | # custom_details: { 186 | # ping_time: "1500ms", 187 | # load_avg: 0.75 188 | # }, 189 | # images: [ 190 | # { 191 | # src: "https://chart.googleapis.com/chart.png", 192 | # href: "https://example.com/", 193 | # alt: "Example text", 194 | # }, 195 | # ], 196 | # links: [ 197 | # { 198 | # href: "https://example.com/", 199 | # text: "Link text", 200 | # }, 201 | # ], 202 | # client: "Sample Monitoring Service", 203 | # client_url: "https://monitoring.example.com" 204 | # ) 205 | # 206 | # @param (see Pagerduty::EventsApiV2#trigger) 207 | # @option (see Pagerduty::EventsApiV2#trigger) 208 | def trigger(details) 209 | if details.key?(:dedup_key) || details.key?(:incident_key) 210 | raise ArgumentError, "incident_key or dedup_key provided, "\ 211 | "please use the EventsApiv2::incident method "\ 212 | "to specify an incident key" 213 | end 214 | 215 | response = api_call("trigger", trigger_request(details)) 216 | @incident_key = response["dedup_key"] 217 | self 218 | end 219 | 220 | # Acknowledge the referenced incident. While an incident is acknowledged, 221 | # it won't generate any additional notifications, even if it receives new 222 | # trigger events. Send PagerDuty an acknowledge event when you know 223 | # someone is presently working on the problem. 224 | # 225 | # @return [Pagerduty::EventsApiV2::Incident] self 226 | # 227 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 228 | # not "success" 229 | # 230 | def acknowledge 231 | api_call("acknowledge") 232 | self 233 | end 234 | 235 | # Resolve the referenced incident. Once an incident is resolved, it won't 236 | # generate any additional notifications. New trigger events with the same 237 | # incident_key as a resolved incident won't re-open the incident. Instead, 238 | # a new incident will be created. Send PagerDuty a resolve event when the 239 | # problem that caused the initial trigger event has been fixed. 240 | # 241 | # @return [Pagerduty::EventsApiV2::Incident] self 242 | # 243 | # @raise [PagerdutyException] If PagerDuty responds with a status that is 244 | # not "success" 245 | # 246 | def resolve 247 | api_call("resolve") 248 | self 249 | end 250 | 251 | private 252 | 253 | PAYLOAD_ATTR = %i[summary timestamp source severity 254 | component group class custom_details].freeze 255 | private_constant :PAYLOAD_ATTR 256 | 257 | def trigger_request(details) 258 | payload = details.select { |key| PAYLOAD_ATTR.include?(key) } 259 | payload[:timestamp] &&= payload[:timestamp].iso8601 260 | request = details.merge(payload: payload) 261 | request.reject! { |key| PAYLOAD_ATTR.include?(key) } 262 | request 263 | end 264 | 265 | def api_call(event_action, payload = {}) 266 | payload = payload.merge( 267 | dedup_key: incident_key, 268 | routing_key: @integration_key, 269 | event_action: event_action, 270 | ) 271 | response = @transport.send_payload(payload) 272 | raise PagerdutyException.new(self, response, response["message"]) unless response["status"] == "success" 273 | 274 | response 275 | end 276 | end 277 | end 278 | end 279 | -------------------------------------------------------------------------------- /spec/pagerduty/events_api_v1_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "spec_helper" 4 | 5 | RSpec.describe Pagerduty do 6 | When(:pagerduty) { 7 | Pagerduty.build( 8 | integration_key: service_key, 9 | api_version: api_version, 10 | http_proxy: http_proxy, 11 | ) 12 | } 13 | 14 | Given(:service_key) { "a-test-service-key" } 15 | Given(:api_version) { 1 } 16 | Given(:http_proxy) { {} } 17 | Given(:transport) { 18 | instance_spy(Pagerduty::HttpTransport, send_payload: standard_response) 19 | } 20 | Given { 21 | allow(Pagerduty::HttpTransport) 22 | .to receive(:new) 23 | .and_return(transport) 24 | } 25 | Given(:standard_response) { 26 | { "status" => "success", "incident_key" => "incident-key-from-response" } 27 | } 28 | 29 | describe ".build" do 30 | context "given version: `1`" do 31 | Given(:api_version) { 1 } 32 | Then { expect(pagerduty).to be_a(Pagerduty::EventsApiV1) } 33 | end 34 | 35 | context "given version: `'1'`" do 36 | Given(:api_version) { "1" } 37 | Then { expect(pagerduty).to be_a(Pagerduty::EventsApiV1) } 38 | end 39 | end 40 | 41 | describe "#trigger" do 42 | describe "sending the API request" do 43 | context "given no options" do 44 | When(:incident) { pagerduty.trigger("a-test-description") } 45 | Then { 46 | expect(transport).to have_received(:send_payload).with( 47 | service_key: "a-test-service-key", 48 | event_type: "trigger", 49 | description: "a-test-description", 50 | ) 51 | } 52 | end 53 | 54 | context "given most options" do 55 | When(:incident) { 56 | pagerduty.trigger( 57 | "a-test-description", 58 | incident_key: "a-test-incident-key", 59 | client: "a-test-client", 60 | client_url: "a-test-client-url", 61 | details: { key: "value" }, 62 | ) 63 | } 64 | Then { 65 | expect(transport).to have_received(:send_payload).with( 66 | service_key: "a-test-service-key", 67 | event_type: "trigger", 68 | description: "a-test-description", 69 | incident_key: "a-test-incident-key", 70 | client: "a-test-client", 71 | client_url: "a-test-client-url", 72 | details: { key: "value" }, 73 | ) 74 | } 75 | end 76 | 77 | context "configured with an HTTP proxy" do 78 | Given(:http_proxy) { 79 | { 80 | host: "test-proxy-host", 81 | port: "test-proxy-port", 82 | username: "test-proxy-username", 83 | password: "test-proxy-password", 84 | } 85 | } 86 | When(:incident) { pagerduty.trigger("a-test-description") } 87 | Then { 88 | expect(Pagerduty::HttpTransport).to have_received(:new).with( 89 | a_hash_including( 90 | proxy: { 91 | host: "test-proxy-host", 92 | port: "test-proxy-port", 93 | username: "test-proxy-username", 94 | password: "test-proxy-password", 95 | }, 96 | ), 97 | ) 98 | } 99 | end 100 | end 101 | 102 | describe "handling responses" do 103 | context "PagerDuty successfully creates the incident" do 104 | Given { 105 | allow(transport).to receive(:send_payload).and_return( 106 | "status" => "success", 107 | "incident_key" => "My Incident Key", 108 | "message" => "Event processed", 109 | ) 110 | } 111 | When(:incident) { pagerduty.trigger("description") } 112 | Then { expect(incident).to be_a Pagerduty::EventsApiV1::Incident } 113 | Then { incident.incident_key == "My Incident Key" } 114 | end 115 | 116 | context "PagerDuty fails to create the incident" do 117 | Given { 118 | allow(transport).to receive(:send_payload).and_return( 119 | "status" => "failure", 120 | "message" => "Event not processed", 121 | ) 122 | } 123 | When(:incident) { pagerduty.trigger("description") } 124 | Then { expect(incident).to have_raised PagerdutyException } 125 | end 126 | 127 | context "PagerDuty responds with HTTP bad request" do 128 | Given { 129 | allow(transport) 130 | .to receive(:send_payload) 131 | .and_raise(Net::HTTPClientException.new(nil, nil)) 132 | } 133 | When(:incident) { pagerduty.trigger("description") } 134 | Then { expect(incident).to have_raised Net::HTTPClientException } 135 | end 136 | end 137 | end 138 | 139 | describe "#incident" do 140 | When(:incident) { pagerduty.incident(incident_key) } 141 | 142 | context "a valid incident_key" do 143 | Given(:incident_key) { "a-test-incident-key" } 144 | Then { expect(incident).to be_a Pagerduty::EventsApiV1::Incident } 145 | Then { incident.incident_key == incident_key } 146 | end 147 | 148 | context "a nil incident_key" do 149 | Given(:incident_key) { nil } 150 | Then { expect(incident).to have_failed ArgumentError } 151 | end 152 | end 153 | 154 | describe Pagerduty::EventsApiV1::Incident do 155 | Given(:incident) { pagerduty.incident(incident_key) } 156 | 157 | Given(:incident_key) { "a-test-incident-key" } 158 | 159 | describe "#acknowledge" do 160 | describe "sending the API request" do 161 | context "given no arguments" do 162 | When(:acknowledge) { incident.acknowledge } 163 | Then { 164 | expect(transport).to have_received(:send_payload).with( 165 | event_type: "acknowledge", 166 | service_key: "a-test-service-key", 167 | incident_key: "a-test-incident-key", 168 | ) 169 | } 170 | end 171 | 172 | context "given a description" do 173 | When(:acknowledge) { incident.acknowledge("test-description") } 174 | Then { 175 | expect(transport).to have_received(:send_payload).with( 176 | event_type: "acknowledge", 177 | service_key: "a-test-service-key", 178 | incident_key: "a-test-incident-key", 179 | description: "test-description", 180 | ) 181 | } 182 | end 183 | 184 | context "given a description and details" do 185 | When(:acknowledge) { 186 | incident.acknowledge("test-description", my: "detail") 187 | } 188 | Then { 189 | expect(transport).to have_received(:send_payload).with( 190 | event_type: "acknowledge", 191 | service_key: "a-test-service-key", 192 | incident_key: "a-test-incident-key", 193 | description: "test-description", 194 | details: { my: "detail" }, 195 | ) 196 | } 197 | end 198 | end 199 | 200 | describe "handling responses" do 201 | context "PagerDuty successfully acknowledges the incident" do 202 | Given { 203 | allow(transport).to receive(:send_payload).and_return( 204 | "status" => "success", 205 | "incident_key" => "a-test-incident-key", 206 | "message" => "Event acknowledged", 207 | ) 208 | } 209 | When(:acknowledge) { incident.acknowledge } 210 | Then { expect(acknowledge).to be incident } 211 | end 212 | 213 | context "PagerDuty fails to acknowledge the incident" do 214 | Given { 215 | allow(transport).to receive(:send_payload).and_return( 216 | "status" => "failure", 217 | "incident_key" => "a-test-incident-key", 218 | "message" => "Event not acknowledged", 219 | ) 220 | } 221 | When(:acknowledge) { incident.acknowledge } 222 | Then { expect(acknowledge).to have_failed PagerdutyException } 223 | end 224 | 225 | context "PagerDuty responds with HTTP bad request" do 226 | Given { 227 | allow(transport) 228 | .to receive(:send_payload) 229 | .and_raise(Net::HTTPClientException.new(nil, nil)) 230 | } 231 | When(:acknowledge) { incident.acknowledge } 232 | Then { expect(acknowledge).to have_failed Net::HTTPClientException } 233 | end 234 | end 235 | end 236 | 237 | describe "#resolve" do 238 | describe "sending the API request" do 239 | context "given no arguments" do 240 | When(:resolve) { incident.resolve } 241 | Then { 242 | expect(transport).to have_received(:send_payload).with( 243 | event_type: "resolve", 244 | service_key: "a-test-service-key", 245 | incident_key: "a-test-incident-key", 246 | ) 247 | } 248 | end 249 | 250 | context "given a description" do 251 | When(:resolve) { incident.resolve("test-description") } 252 | Then { 253 | expect(transport).to have_received(:send_payload).with( 254 | event_type: "resolve", 255 | service_key: "a-test-service-key", 256 | incident_key: "a-test-incident-key", 257 | description: "test-description", 258 | ) 259 | } 260 | end 261 | 262 | context "given a description and details" do 263 | When(:resolve) { incident.resolve("test-description", my: "detail") } 264 | Then { 265 | expect(transport).to have_received(:send_payload).with( 266 | event_type: "resolve", 267 | service_key: "a-test-service-key", 268 | incident_key: "a-test-incident-key", 269 | description: "test-description", 270 | details: { my: "detail" }, 271 | ) 272 | } 273 | end 274 | end 275 | 276 | describe "handling responses" do 277 | context "PagerDuty successfully resolves the incident" do 278 | Given { 279 | allow(transport).to receive(:send_payload).and_return( 280 | "status" => "success", 281 | "incident_key" => "a-test-incident-key", 282 | "message" => "Event resolved", 283 | ) 284 | } 285 | When(:resolve) { incident.resolve } 286 | Then { expect(resolve).to be incident } 287 | end 288 | 289 | context "PagerDuty fails to create the incident" do 290 | Given { 291 | allow(transport).to receive(:send_payload).and_return( 292 | "status" => "failure", 293 | "message" => "Event not resolved", 294 | ) 295 | } 296 | When(:resolve) { incident.resolve } 297 | Then { expect(resolve).to have_failed PagerdutyException } 298 | end 299 | 300 | context "PagerDuty responds with HTTP bad request" do 301 | Given { 302 | allow(transport) 303 | .to receive(:send_payload) 304 | .and_raise(Net::HTTPClientException.new(nil, nil)) 305 | } 306 | When(:resolve) { incident.resolve } 307 | Then { expect(resolve).to have_failed Net::HTTPClientException } 308 | end 309 | end 310 | end 311 | 312 | describe "#trigger" do 313 | describe "sending the API request" do 314 | context "given no options" do 315 | Given(:incident_key) { "instance incident_key" } 316 | When(:trigger) { incident.trigger("description") } 317 | Then { 318 | expect(transport).to have_received(:send_payload).with( 319 | incident_key: "instance incident_key", 320 | service_key: "a-test-service-key", 321 | event_type: "trigger", 322 | description: "description", 323 | ) 324 | } 325 | end 326 | 327 | context "given a incident_key option" do 328 | When(:trigger) { incident.trigger("desc", incident_key: "key") } 329 | Then { expect(trigger).to have_failed ArgumentError } 330 | end 331 | end 332 | end 333 | end 334 | end 335 | -------------------------------------------------------------------------------- /spec/pagerduty/events_api_v2_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "spec_helper" 4 | 5 | RSpec.describe Pagerduty do 6 | When(:pagerduty) { 7 | Pagerduty.build( 8 | integration_key: integration_key, 9 | api_version: api_version, 10 | http_proxy: http_proxy, 11 | ) 12 | } 13 | 14 | Given(:integration_key) { "a-test-routing-key" } 15 | Given(:api_version) { 2 } 16 | Given(:http_proxy) { nil } 17 | Given(:transport) { 18 | instance_spy(Pagerduty::HttpTransport, send_payload: standard_response) 19 | } 20 | Given { 21 | allow(Pagerduty::HttpTransport) 22 | .to receive(:new) 23 | .and_return(transport) 24 | } 25 | Given(:standard_response) { 26 | { 27 | "status" => "success", 28 | "message" => "MY MESSAGE", 29 | "dedup_key" => "my-incident-key", 30 | } 31 | } 32 | Given(:simple_incident_details) { 33 | { 34 | summary: "summary", 35 | source: "source", 36 | severity: "critical", 37 | } 38 | } 39 | 40 | describe ".build" do 41 | context "given version: `2`" do 42 | Given(:api_version) { 2 } 43 | Then { expect(pagerduty).to be_a(Pagerduty::EventsApiV2) } 44 | end 45 | 46 | context "given version: `'2'`" do 47 | Given(:api_version) { "2" } 48 | Then { expect(pagerduty).to be_a(Pagerduty::EventsApiV2) } 49 | end 50 | end 51 | 52 | describe "#trigger" do 53 | describe "sending the API request" do 54 | context "given no event details" do 55 | When(:incident) { pagerduty.trigger } 56 | Then { expect(incident).to have_raised ArgumentError } 57 | end 58 | 59 | context "given all event details" do 60 | When(:incident) { 61 | pagerduty.trigger( 62 | summary: "summary", 63 | source: "source", 64 | severity: "critical", 65 | timestamp: Time.iso8601("2015-07-17T08:42:58Z"), 66 | component: "component", 67 | group: "group", 68 | class: "class", 69 | custom_details: { 70 | random: "random", 71 | }, 72 | images: [{ 73 | src: "http://via.placeholder.com/350x150", 74 | href: "https://example.com/", 75 | alt: "Example text", 76 | }], 77 | links: [{ 78 | href: "https://example.com/", 79 | text: "Link text", 80 | }], 81 | client: "Sample Monitoring Service", 82 | client_url: "https://monitoring.example.com", 83 | ) 84 | } 85 | Then { 86 | expect(transport).to have_received(:send_payload).with( 87 | routing_key: "a-test-routing-key", 88 | event_action: "trigger", 89 | dedup_key: nil, 90 | payload: { 91 | summary: "summary", 92 | source: "source", 93 | severity: "critical", 94 | timestamp: "2015-07-17T08:42:58Z", 95 | component: "component", 96 | group: "group", 97 | class: "class", 98 | custom_details: { 99 | random: "random", 100 | }, 101 | }, 102 | images: [{ 103 | src: "http://via.placeholder.com/350x150", 104 | href: "https://example.com/", 105 | alt: "Example text", 106 | }], 107 | links: [{ 108 | href: "https://example.com/", 109 | text: "Link text", 110 | }], 111 | client: "Sample Monitoring Service", 112 | client_url: "https://monitoring.example.com", 113 | ) 114 | } 115 | end 116 | 117 | context "given simple event details" do 118 | When { pagerduty.trigger(simple_incident_details) } 119 | Then { 120 | expect(transport).to have_received(:send_payload).with( 121 | routing_key: "a-test-routing-key", 122 | dedup_key: nil, 123 | event_action: "trigger", 124 | payload: { 125 | summary: "summary", 126 | source: "source", 127 | severity: "critical", 128 | }, 129 | ) 130 | } 131 | end 132 | 133 | context "given a dedup_key option" do 134 | When(:trigger) { 135 | pagerduty.trigger(simple_incident_details.merge(dedup_key: "key")) 136 | } 137 | Then { expect(trigger).to have_failed ArgumentError } 138 | end 139 | 140 | context "given an incident_key option" do 141 | When(:trigger) { 142 | pagerduty.trigger(simple_incident_details.merge(incident_key: "key")) 143 | } 144 | Then { expect(trigger).to have_failed ArgumentError } 145 | end 146 | 147 | context "configured with an HTTP proxy" do 148 | Given(:http_proxy) { 149 | { 150 | host: "test-proxy-host", 151 | port: "test-proxy-port", 152 | username: "test-proxy-username", 153 | password: "test-proxy-password", 154 | } 155 | } 156 | When(:incident) { pagerduty.trigger(simple_incident_details) } 157 | Then { 158 | expect(Pagerduty::HttpTransport).to have_received(:new).with( 159 | a_hash_including( 160 | proxy: { 161 | host: "test-proxy-host", 162 | port: "test-proxy-port", 163 | username: "test-proxy-username", 164 | password: "test-proxy-password", 165 | }, 166 | ), 167 | ) 168 | } 169 | end 170 | end 171 | 172 | describe "handling responses" do 173 | context "PagerDuty successfully creates the incident" do 174 | Given { 175 | allow(transport).to receive(:send_payload).and_return( 176 | "status" => "success", 177 | "dedup_key" => "incident-key-in-response", 178 | "message" => "Event processed", 179 | ) 180 | } 181 | When(:incident) { pagerduty.trigger(simple_incident_details) } 182 | Then { expect(incident).to be_a Pagerduty::EventsApiV2::Incident } 183 | Then { incident.incident_key == "incident-key-in-response" } 184 | end 185 | 186 | context "PagerDuty fails to create the incident" do 187 | Given { 188 | allow(transport).to receive(:send_payload).and_return( 189 | "status" => "invalid event", 190 | "message" => "Event object is invalid", 191 | ) 192 | } 193 | When(:incident) { pagerduty.trigger(simple_incident_details) } 194 | Then { expect(incident).to have_raised PagerdutyException } 195 | end 196 | 197 | context "PagerDuty responds with HTTP bad request" do 198 | Given { 199 | allow(transport) 200 | .to receive(:send_payload) 201 | .and_raise(Net::HTTPClientException.new(nil, nil)) 202 | } 203 | When(:incident) { pagerduty.trigger(simple_incident_details) } 204 | Then { expect(incident).to have_raised Net::HTTPClientException } 205 | end 206 | end 207 | end 208 | 209 | describe "#incident" do 210 | When(:incident) { pagerduty.incident(incident_key) } 211 | 212 | context "a valid incident_key" do 213 | Given(:incident_key) { "a-test-incident-key" } 214 | Then { expect(incident).to be_a Pagerduty::EventsApiV2::Incident } 215 | Then { incident.incident_key == incident_key } 216 | end 217 | 218 | context "a nil incident_key" do 219 | Given(:incident_key) { nil } 220 | Then { expect(incident).to have_failed ArgumentError } 221 | end 222 | end 223 | 224 | describe Pagerduty::EventsApiV2::Incident do 225 | Given(:incident) { pagerduty.incident(incident_key) } 226 | Given(:incident_key) { "a-test-incident-key" } 227 | 228 | describe "#acknowledge" do 229 | describe "sending the API request" do 230 | context "given no event details" do 231 | When { incident.acknowledge } 232 | Then { 233 | expect(transport).to have_received(:send_payload).with( 234 | event_action: "acknowledge", 235 | routing_key: "a-test-routing-key", 236 | dedup_key: "a-test-incident-key", 237 | ) 238 | } 239 | end 240 | 241 | context "given a single string arg (like in V1)" do 242 | When(:acknowledge) { incident.acknowledge("test-description") } 243 | Then { expect(acknowledge).to have_failed ArgumentError } 244 | end 245 | 246 | context "given a string arg and options hash (like in V1)" do 247 | When(:acknowledge) { 248 | incident.acknowledge("test-description", my: "detail") 249 | } 250 | Then { expect(acknowledge).to have_failed ArgumentError } 251 | end 252 | end 253 | 254 | describe "handling API responses" do 255 | context "PagerDuty successfully acknowledges the incident" do 256 | When(:acknowledge) { incident.acknowledge } 257 | Then { expect(acknowledge).to be incident } 258 | end 259 | 260 | context "PagerDuty fails to acknowledge the incident" do 261 | Given { 262 | allow(transport).to receive(:send_payload).and_return( 263 | "status" => "invalid event", 264 | "message" => "Event object is invalid", 265 | ) 266 | } 267 | When(:acknowledge) { incident.acknowledge } 268 | Then { expect(acknowledge).to have_failed PagerdutyException } 269 | end 270 | 271 | context "PagerDuty responds with HTTP bad request" do 272 | Given { 273 | allow(transport) 274 | .to receive(:send_payload) 275 | .and_raise(Net::HTTPClientException.new(nil, nil)) 276 | } 277 | When(:acknowledge) { incident.acknowledge } 278 | Then { expect(acknowledge).to have_failed Net::HTTPClientException } 279 | end 280 | end 281 | end 282 | 283 | describe "#resolve" do 284 | describe "sending the API request" do 285 | context "given no event details" do 286 | When(:resolve) { incident.resolve } 287 | Then { 288 | expect(transport).to have_received(:send_payload).with( 289 | event_action: "resolve", 290 | routing_key: "a-test-routing-key", 291 | dedup_key: "a-test-incident-key", 292 | ) 293 | } 294 | end 295 | 296 | context "a description" do 297 | When(:resolve) { incident.resolve("test-description") } 298 | Then { expect(resolve).to have_failed ArgumentError } 299 | end 300 | 301 | context "a description and details" do 302 | When(:resolve) { incident.resolve("test-description", my: "detail") } 303 | Then { expect(resolve).to have_failed ArgumentError } 304 | end 305 | end 306 | 307 | describe "handling API responses" do 308 | context "PagerDuty successfully resolves the incident" do 309 | When(:resolve) { incident.resolve } 310 | Then { expect(resolve).to be incident } 311 | end 312 | 313 | context "PagerDuty fails to create the incident" do 314 | Given { 315 | allow(transport).to receive(:send_payload).and_return( 316 | "status" => "invalid event", 317 | "message" => "Event object is invalid", 318 | ) 319 | } 320 | When(:resolve) { incident.resolve } 321 | Then { expect(resolve).to have_failed PagerdutyException } 322 | end 323 | 324 | context "PagerDuty responds with HTTP bad request" do 325 | Given { 326 | allow(transport) 327 | .to receive(:send_payload) 328 | .and_raise(Net::HTTPClientException.new(nil, nil)) 329 | } 330 | When(:resolve) { incident.resolve } 331 | Then { expect(resolve).to have_failed Net::HTTPClientException } 332 | end 333 | end 334 | end 335 | 336 | describe "#trigger" do 337 | describe "sending the API request" do 338 | context "given no arguments" do 339 | When(:trigger) { incident.trigger } 340 | Then { expect(trigger).to have_failed ArgumentError } 341 | end 342 | 343 | context "given simple event details" do 344 | Given(:incident_key) { "instance incident_key" } 345 | When(:trigger) { incident.trigger(simple_incident_details) } 346 | Then { 347 | expect(transport).to have_received(:send_payload).with( 348 | routing_key: "a-test-routing-key", 349 | dedup_key: "instance incident_key", 350 | event_action: "trigger", 351 | payload: { 352 | summary: "summary", 353 | source: "source", 354 | severity: "critical", 355 | }, 356 | ) 357 | } 358 | end 359 | 360 | context "given a dedup_key option" do 361 | When(:trigger) { 362 | incident.trigger(simple_incident_details.merge(dedup_key: "key")) 363 | } 364 | Then { expect(trigger).to have_failed ArgumentError } 365 | end 366 | 367 | context "given an incident_key option" do 368 | When(:trigger) { 369 | incident.trigger(simple_incident_details.merge(incident_key: "key")) 370 | } 371 | Then { expect(trigger).to have_failed ArgumentError } 372 | end 373 | end 374 | end 375 | end 376 | end 377 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog], and this project adheres to 6 | [Semantic Versioning]. 7 | 8 | [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ 9 | [Semantic Versioning]: https://semver.org/spec/v2.0.0.html 10 | 11 | ## [Unreleased] 12 | 13 | [Unreleased]: https://github.com/envato/pagerduty/compare/v4.0.1...HEAD 14 | 15 | ## [4.0.1] - 2024-02-17 16 | 17 | ### Added 18 | 19 | - Test on Ruby 3.2, and 3.3 in the CI build ([#84], [#85]). 20 | 21 | ### Changed 22 | 23 | - Updated authorship details ([#82]). 24 | - Bump the Rubocop development dependency to version 1 ([#86]). 25 | 26 | [4.0.1]: https://github.com/envato/pagerduty/compare/v4.0.0...v4.0.1 27 | [#82]: https://github.com/envato/pagerduty/pull/82 28 | [#84]: https://github.com/envato/pagerduty/pull/84 29 | [#85]: https://github.com/envato/pagerduty/pull/85 30 | [#86]: https://github.com/envato/pagerduty/pull/86 31 | 32 | ## [4.0.0] - 2022-02-14 33 | 34 | ### Removed 35 | 36 | - Remove support for Ruby versions lower than 2.3 ([#78]). 37 | 38 | - Removed the deprecated way of creating a Pagerduty instance ([#79]). 39 | 40 | ```diff 41 | - pagerduty = Pagerduty.new("") 42 | + pagerduty = Pagerduty.build(integration_key: "", api_version: 1) 43 | pagerduty.trigger("") 44 | incident.acknowledge 45 | incident.resolve 46 | ``` 47 | 48 | - Removed the deprecated `get_incident` and `service_key` methods from the 49 | `Pagerduty::EventsApiV1::Incident` class ([#79]). `incident` provides a replacement 50 | for the `get_incident` method. The `service_key` method has no replacement. 51 | 52 | ```diff 53 | pagerduty = Pagerduty.build(integration_key: "", api_version: 1) 54 | - incident = pagerduty.get_incident("") 55 | + incident = pagerduty.incident("") 56 | incident.trigger("") 57 | incident.acknowledge 58 | incident.resolve 59 | ``` 60 | 61 | - Removed the `PagerdutyIncident` class. Instead, use the 62 | `Pagerduty::EventsApiV1::Incident` class ([#79]). 63 | 64 | ### Added 65 | 66 | - Test on Ruby 3.0 and 3.1 in CI build ([#74], [#80]). 67 | 68 | ### Changed 69 | 70 | - The explicit `json` gem runtime dependency has been removed ([#78]). 71 | - Use GitHub Actions for CI build instead of TravisCI ([#73]). 72 | - The default git branch has been renamed to `main` ([#77]). 73 | 74 | ### Fixed 75 | 76 | - Resolved `Net::HTTPServerException` deprecation warning in test suite([#75]). 77 | 78 | [4.0.0]: https://github.com/envato/pagerduty/compare/v3.0.0...v4.0.0 79 | [#73]: https://github.com/envato/pagerduty/pull/73 80 | [#74]: https://github.com/envato/pagerduty/pull/74 81 | [#75]: https://github.com/envato/pagerduty/pull/75 82 | [#77]: https://github.com/envato/pagerduty/pull/77 83 | [#78]: https://github.com/envato/pagerduty/pull/78 84 | [#79]: https://github.com/envato/pagerduty/pull/79 85 | [#80]: https://github.com/envato/pagerduty/pull/80 86 | 87 | ## [3.0.0] - 2020-04-20 88 | 89 | ### Added 90 | 91 | - A new mechanism for instantiating a Pagerduty instance ([#64]). 92 | 93 | ```ruby 94 | pagerduty = Pagerduty.build(integration_key: "", 95 | api_version: 1) 96 | ``` 97 | 98 | This new method will return an instance that implements requested PagerDuty 99 | Events API version. 100 | 101 | - Support for the [Pagerduty Events API version 2][events-v2-docs] ([#66]). 102 | 103 | ```ruby 104 | pagerduty = Pagerduty.build( 105 | integration_key: "", 106 | api_version: 2 107 | ) 108 | incident = pagerduty.trigger( 109 | summary: "summary", 110 | source: "source", 111 | severity: "critical" 112 | ) 113 | incident.acknowledge 114 | incident.resolve 115 | ``` 116 | 117 | - Added an `incident` method to the Pagerduty Events API V1 instance ([#67]). 118 | This is intended as a drop-in replacement for the now-deprecated 119 | `get_incident` method. 120 | 121 | ```ruby 122 | pagerduty = Pagerduty.build( 123 | integration_key: "", 124 | api_version: 1 125 | ) 126 | incident = pagerduty.incident("") 127 | ``` 128 | 129 | ### Deprecated 130 | 131 | - Using `new` on `Pagerduty` ([#64]). This works, but will be removed in the 132 | next major version. 133 | 134 | ```diff 135 | - pagerduty = Pagerduty.new("") 136 | + pagerduty = Pagerduty.build(integration_key: "", api_version: 1) 137 | pagerduty.trigger("") 138 | incident.acknowledge 139 | incident.resolve 140 | ``` 141 | 142 | Instead, use the new `Pagerduty.build` method (see above). 143 | 144 | - The `get_incident` method is now deprecated ([#67]). It still works, but 145 | will be removed in the next major release. Please migrate to the new 146 | `incident` method, that works in exactly the same way. 147 | 148 | ```diff 149 | pagerduty = Pagerduty.new("") 150 | - incident = pagerduty.get_incident("") 151 | + incident = pagerduty.incident("") 152 | incident.trigger("") 153 | incident.acknowledge 154 | incident.resolve 155 | ``` 156 | 157 | ### Changed 158 | 159 | - `Pagerduty` is no longer a class. It's now a Ruby module ([#64]). This will 160 | break implementations that use `Pagerduty` in their inheritance tree. 161 | 162 | - `PagerdutyIncident` no-longer inherits from `Pagerduty` and its initializer 163 | parameters have changed ([#64]). Actually, it's now an alias of the 164 | `Pagerduty::EventsApiV1:Incident` class. 165 | 166 | ### Removed 167 | 168 | - Can no longer specify a new incident key when triggering from an `Incident` 169 | object ([#64]). 170 | 171 | This will now raise an `ArgumentError`. eg. 172 | 173 | ```ruby 174 | incident1 = pagerduty.trigger('first incident', incident_key: 'one') # this'll work fine 175 | incident2 = incident1.trigger('second incident', incident_key: 'two') # this no-longer works. 176 | ``` 177 | 178 | The difference is in the object we're calling the method on. 179 | 180 | Instead always use the pagerduty object when triggering new incidents (with 181 | new incident keys). 182 | 183 | This works with the Events API V1: 184 | 185 | ```ruby 186 | incident1 = pagerduty.trigger('first incident', incident_key: 'one') 187 | incident2 = pagerduty.trigger('second incident', incident_key: 'two') 188 | ``` 189 | 190 | And this is even better, as it works with both the Events API V1 and V2: 191 | 192 | ```ruby 193 | incident1 = pagerduty.incident('one').trigger('first incident') 194 | incident2 = pagerduty.incident('two').trigger('second incident') 195 | ``` 196 | 197 | [3.0.0]: https://github.com/envato/pagerduty/compare/v2.1.3...v3.0.0 198 | [events-v2-docs]: https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview 199 | [#64]: https://github.com/envato/pagerduty/pull/64 200 | [#66]: https://github.com/envato/pagerduty/pull/66 201 | [#67]: https://github.com/envato/pagerduty/pull/67 202 | 203 | ## [2.1.3] - 2020-02-10 204 | 205 | ### Added 206 | 207 | - Test against Ruby 2.6 and 2.7 in CI ([#55], [#63]). 208 | 209 | - Add project metadata to the gemspec ([#57]). 210 | 211 | - A downloads badge to the readme ([#58]). 212 | 213 | - This changelog document ([#59]). 214 | 215 | ### Fixed 216 | 217 | - Realign with latest Rubocop style ([#54], [#62]). 218 | 219 | ### Removed 220 | 221 | - Test files are no longer included in the gem file ([#60]). 222 | 223 | [2.1.3]: https://github.com/envato/pagerduty/compare/v2.1.2...v2.1.3 224 | [#54]: https://github.com/envato/pagerduty/pull/54 225 | [#55]: https://github.com/envato/pagerduty/pull/55 226 | [#57]: https://github.com/envato/pagerduty/pull/57 227 | [#58]: https://github.com/envato/pagerduty/pull/58 228 | [#59]: https://github.com/envato/pagerduty/pull/59 229 | [#60]: https://github.com/envato/pagerduty/pull/60 230 | [#62]: https://github.com/envato/pagerduty/pull/62 231 | [#63]: https://github.com/envato/pagerduty/pull/63 232 | 233 | ## [2.1.2] - 2018-09-18 234 | 235 | ### Fixed 236 | 237 | - Remove leading and trailing whitespace in the gem post-install message 238 | ([#53]). 239 | 240 | - Realign with latest Rubocop style ([#53]). 241 | 242 | - Update the links to Pagerduty official API documentation. 243 | 244 | - Don't specify Ruby patch versions in Travis CI build configuration. Remove 245 | the toil involved in keeping these up to date. 246 | 247 | ### Removed 248 | 249 | - Remove gem post-install message. 250 | 251 | [2.1.2]: https://github.com/envato/pagerduty/compare/v2.1.1...v2.1.2 252 | [#53]: https://github.com/envato/pagerduty/pull/53 253 | 254 | ## [2.1.1] - 2018-03-06 255 | 256 | ### Added 257 | 258 | - Test against Ruby 2.4 and 2.5 in CI ([#51]). 259 | 260 | ### Fixed 261 | 262 | - Removed version restrictions on the `rubocop` development dependency, and 263 | realigned code with the latest version. ([#51]). 264 | 265 | ### Removed 266 | 267 | - Ruby 1.9.3 and Ruby 2.0.0 from the CI build ([#51]). 268 | 269 | [2.1.1]: https://github.com/envato/pagerduty/compare/v2.1.0...v2.1.1 270 | [#51]: https://github.com/envato/pagerduty/pull/51 271 | 272 | ## [2.1.0] - 2016-01-19 273 | 274 | ### Added 275 | 276 | - Rubocop configuration and align syntax ([#32], [#35], [#38], [#43]). 277 | 278 | - Documentation describing how to resolve an existing incident ([#34]). 279 | 280 | - Print Ruby warnings during the CI build ([#36]). 281 | 282 | - Remove restrictions on `bundler` development dependency ([#37]). 283 | 284 | - Test against Ruby 2.3 in CI ([#41]). 285 | 286 | - Add support for HTTP proxy ([#47]). 287 | 288 | ### Fixed 289 | 290 | - `Pagerduty#get_incident` raises `ArgumentError` if the provided incident key 291 | is `nil` ([#46]). 292 | 293 | [2.1.0]: https://github.com/envato/pagerduty/compare/v2.0.1...v2.1.0 294 | [#32]: https://github.com/envato/pagerduty/pull/32 295 | [#34]: https://github.com/envato/pagerduty/pull/34 296 | [#35]: https://github.com/envato/pagerduty/pull/35 297 | [#36]: https://github.com/envato/pagerduty/pull/36 298 | [#37]: https://github.com/envato/pagerduty/pull/37 299 | [#38]: https://github.com/envato/pagerduty/pull/38 300 | [#41]: https://github.com/envato/pagerduty/pull/41 301 | [#43]: https://github.com/envato/pagerduty/pull/43 302 | [#46]: https://github.com/envato/pagerduty/pull/46 303 | [#47]: https://github.com/envato/pagerduty/pull/47 304 | 305 | ## [2.0.1] - 2015-03-29 306 | 307 | ### Added 308 | 309 | - Moved specs to RSpec 3.0 ([#30]). 310 | 311 | - Test against Ruby 2.2 in CI ([#31]). 312 | 313 | ### Changed 314 | 315 | - Rename `HttpTransport#send` to `httpTransport#send_payload`. This avoids 316 | shawdowing `Object#send` ([#29]). 317 | 318 | [2.0.1]: https://github.com/envato/pagerduty/compare/v2.0.0...v2.0.1 319 | [#29]: https://github.com/envato/pagerduty/pull/29 320 | [#30]: https://github.com/envato/pagerduty/pull/30 321 | [#31]: https://github.com/envato/pagerduty/pull/31 322 | 323 | ## [2.0.0] - 2014-05-26 324 | 325 | ### Added 326 | 327 | - The `description` argument in the `PagerdutyIncident#acknowledge` and 328 | `PagerdutyIncident#resolve` methods is now optional. As specified by the 329 | API ([#19]). 330 | 331 | - YARD class and method documentation. In addition to updating the readme 332 | ([#20]). 333 | 334 | ### Changed 335 | 336 | - `Pagerduty#trigger` arguments have changed to accept all available options 337 | provided by the API, rather than just `details` ([#19]). 338 | 339 | ```ruby 340 | pagerduty.trigger( 341 | "incident description", 342 | incident_key: "my unique incident identifier", 343 | client: "server in trouble", 344 | client_url: "http://server.in.trouble", 345 | details: {my: "extra details"}, 346 | ) 347 | ``` 348 | 349 | This is breaking in the case where providing a `details` hash: 350 | 351 | ```ruby 352 | # This no longer works post v2.0.0. If you're 353 | # providing details in this form, please migrate. 354 | pagerduty.trigger("desc", key: "value") 355 | 356 | # Post v2.0.0 this is how to send details (migrate to this please). 357 | pagerduty.trigger("desc", details: {key: "value"}) 358 | ``` 359 | 360 | - `Pagerduty` class initialiser no longer accepts an `incident_key`. This 361 | attribute can now be provided when calling the `#trigger` method (see above) 362 | ([#19]). 363 | 364 | - `PagerdutyException` now extends from `StandardError` rather than 365 | `Exception`. This may affect how you rescue the error. i.e. `rescue 366 | StandardError` will now rescue a `PagerdutyException` where it did not 367 | before ([#21]). 368 | 369 | [2.0.0]: https://github.com/envato/pagerduty/compare/v1.4.1...v2.0.0 370 | [#19]: https://github.com/envato/pagerduty/pull/19 371 | [#20]: https://github.com/envato/pagerduty/pull/20 372 | [#21]: https://github.com/envato/pagerduty/pull/21 373 | 374 | ## [1.4.1] - 2014-05-21 375 | 376 | ### Added 377 | 378 | - Set HTTP open and read timeouts to 60 seconds ([#16]). 379 | 380 | - Add tests and a CI build (TravisCI) ([#17]). 381 | 382 | - Add `bundler`, `rake` and `rspec-given` as development dependencies ([#17]). 383 | 384 | - Extract (private) `HttpTransport` class, for single responsibility and ease 385 | of testing ([#18]). 386 | 387 | ### Changed 388 | 389 | - Raise errors instead of throwing them ([#17]). 390 | 391 | ### Fixed 392 | 393 | - Use the OS default `CA path` rather than explicitly setting one ([#18]). 394 | 395 | [1.4.1]: https://github.com/envato/pagerduty/compare/v1.4.0...v1.4.1 396 | [#16]: https://github.com/envato/pagerduty/pull/16 397 | [#17]: https://github.com/envato/pagerduty/pull/17 398 | [#18]: https://github.com/envato/pagerduty/pull/18 399 | 400 | ## [1.4.0] - 2014-04-02 401 | 402 | ### Added 403 | 404 | - Support TLS to the Pagerduty API ([#14]). 405 | 406 | [1.4.0]: https://github.com/envato/pagerduty/compare/v1.3.4...v1.4.0 407 | [#14]: https://github.com/envato/pagerduty/pull/14 408 | 409 | ## [1.3.4] - 2013-02-12 410 | 411 | ### Fixed 412 | 413 | - Update `Rakefile` to word with recent versions of RDoc 414 | 415 | ### Changed 416 | 417 | - Enforce `json` gem to versions 1.7.7 and above. 418 | 419 | [1.3.4]: https://github.com/envato/pagerduty/compare/v1.3.3...v1.3.4 420 | 421 | ## [1.3.3] - 2012-12-12 422 | 423 | ### Changed 424 | 425 | - Allow `json` gem versions 1.5 and above. 426 | 427 | [1.3.3]: https://github.com/envato/pagerduty/compare/v1.3.2...v1.3.3 428 | 429 | ## [1.3.2] - 2012-10-31 430 | 431 | ### Fixed 432 | 433 | - `details` are now correctly passed in the API call. 434 | 435 | [1.3.2]: https://github.com/envato/pagerduty/compare/v1.3.1...v1.3.2 436 | 437 | ## [1.3.1] - 2012-10-19 438 | 439 | ### Fixed 440 | 441 | - Consolidated all Pagerduty definitions to be a `class`. 442 | 443 | [1.3.1]: https://github.com/envato/pagerduty/compare/v1.3.0...v1.3.1 444 | 445 | ## [1.3.0] - 2012-10-18 446 | 447 | ### Added 448 | 449 | - Add ability to set the incident key via the initializer. 450 | 451 | ### Changed 452 | 453 | - Perform release with `bundler` instead of `jeweller`. 454 | - Manage all gem dependencies in the gemspec file. 455 | - Remove dependency on `curb`. 456 | 457 | [1.3.0]: https://github.com/envato/pagerduty/compare/v1.1.1...v1.3.0 458 | 459 | ## [1.1.1] - 2010-11-17 460 | 461 | ### Fixed 462 | 463 | - Specify `json` and `curb` gems as dependencies in the default gem group. 464 | 465 | ### Added 466 | 467 | - Prevent the `.bundle` directory from being added to the git repository. 468 | 469 | [1.1.1]: https://github.com/envato/pagerduty/compare/v1.1.0...v1.1.1 470 | 471 | ## [1.1.0] - 2010-11-16 472 | 473 | ### Added 474 | 475 | - New `Pagerduty#get_incident` method. 476 | 477 | [1.1.0]: https://github.com/envato/pagerduty/compare/v1.0.1...v1.1.0 478 | 479 | ## [1.0.1] - 2010-11-16 480 | 481 | ### Fixed 482 | 483 | - Specify `json` and `curb` gems as dependencies. 484 | 485 | [1.0.1]: https://github.com/envato/pagerduty/compare/v1.0.0...v1.0.1 486 | 487 | ## [1.0.0] - 2010-11-16 488 | 489 | ### Added 490 | 491 | - Library released as Open Source! 492 | 493 | [1.0.0]: https://github.com/envato/pagerduty/releases/tag/v1.0.0 494 | --------------------------------------------------------------------------------