PagerDuty API を試してデータを理解する

タイトルの通りで，PagerDuty API を使ってみて，ドキュメントを読んでデータを理解する

## 前提 / 準備

- Docker
    - Ruby
- PagerDuty account / API Key

```shell-session
$ docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app ruby:3.2.2-bookworm bundle init
$ edit Gemfile
$ docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app ruby:3.2.2-bookworm bundle install 
$ edit compose.yml Dockerfile
```

```compose.yml
version: '3'
services:
  app:
    build: .
    volumes:
      - .:/usr/src/app
```

```dockerfile:Dockerfile
FROM ruby:3.2.2-bookworm

WORKDIR /usr/src/app

COPY Gemfile Gemfile.lock /usr/src/app/
RUN bundle install
```

```rb:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

gem 'dotenv'
gem "pager_duty-connection"

```

## API 使ってみる

```
$ edit .env a.rb
$ sed -e 's/=.*/=/' .env
PAGERDUTY_TOKEN=
```

```a.rb
#!/usr/bin/env ruby

# https://github.com/technicalpickles/pager_duty-connection/blob/master/examples/find-users.rb

require 'dotenv/load'
require 'pager_duty/connection'

token = ENV['PAGERDUTY_TOKEN'] || raise("Missing ENV['PAGERDUTY_TOKEN']")

pagerduty = PagerDuty::Connection.new(token)

response = pagerduty.get('users')
response['users'].each do |user|
  puts "#{user['name']}: #{user['email']}"
end
```

```
$ docker compose run --rm app ./a.rb
(snip)
```

これで準備ができました．

## ドキュメントを読む

- https://developer.pagerduty.com/docs/ZG9jOjQ2NDA2-introduction
    - Build Apps & Integrations
        - https://developer.pagerduty.com/docs/a47c3f8e79f21-api-picker
            - API Picker とか case が書いてあって良さそう
    - REST API
        - REST / Events で分かれてて，Events の方はイベントを送ったりする方らしい．
        - 今回はコチラを見る．
    - Events API v2
    - Events API v1
    - Webhooks
- https://developer.pagerduty.com/api-reference/f1a95bb9397ba-changelog
    - https://developer.pagerduty.com/api-reference/a47605517c19a-api-concepts
        - API Concepts に色々ありそう

### データ

```plantuml
EscalationPolicy -- User
EscalationPolicy -- Schedule
EscalationPolicy -- "*" Incident
(EscalationPolicy, Incident) .. Service
Incident -- "*" Notification 
Incident -- "*" LogEntry

class Service {
}
note right of Service: 監視対象
```

### 試してみる

なんとなくわかったので試してみる

- 起点は EscaltionPolicy にあると考えると，
   - EscalationPolicy
        - User
        - Schedule

の辺りを確認できると静的な部分の全容把握がやりやすそう．

<details><summary>ソース</summary>

```rb
#!/usr/bin/env ruby

require 'dotenv/load'
require 'pager_duty/connection'

def token
  @token ||= ENV['PAGERDUTY_TOKEN'] || raise("Missing ENV['PAGERDUTY_TOKEN']")
end

def debug
  true
end

def pagerduty
  @pagerduty ||=  PagerDuty::Connection.new(token)
end

def get_list(url:, key: nil)
  key ||= url
  if debug
    puts "GET #{url}"
  end
  response = pagerduty.get(url)
  list = []
  response[key].each do |e|
    item = e
    if block_given?
      item = yield(e)
    end
    list << item
  end
  list
end

module BaseFromHash
  def self.included(klass)
    klass.extend ClassMethods
  end

  module ClassMethods
    def from_hash(hash)
      params = hash
      if block_given?
        params = yield(hash)
      end
      new(**params.slice(*members))
    end
  end
end

Team = Struct.new(:id, :name, keyword_init: true) do
  include BaseFromHash

  def to_s
    "Team: #{name}"
  end
end

User = Struct.new(:summary, keyword_init: true) do
  include BaseFromHash

  def to_s
    "User: #{summary}"
  end
end

EscalationPolicy = Struct.new(:id, :name, :escalation_rules, :teams, keyword_init: true) do
  include BaseFromHash

  def to_s
    "EscalationPolicy: #{name}"
  end
end

Schedule = Struct.new(:id, :summary, keyword_init: true) do
  include BaseFromHash

  def to_s
    "Schedule: #{summary}"
  end
end

def main
  escalation_policies = get_list(url: 'escalation_policies') do |escalation_policy|
    EscalationPolicy.from_hash(escalation_policy) do |policy|
      policy[:escalation_rules] = policy[:escalation_rules].map do |rule|
        {
          targets: rule['targets'].map do |target|
            if target['type'] == 'user_reference'
              User.from_hash(target)
            elsif target['type'] == 'schedule_reference'
              Schedule.from_hash(target)
            end
          end
        }
      end
      policy[:teams] = policy[:teams].map { |team| Team.from_hash(team) }
      policy
    end
  end
  escalation_policies.each do |escalation_policy|
    puts escalation_policy
    escalation_policy.teams.each do |team|
      puts team
    end
    escalation_policy.escalation_rules.each do |rule|
      rule[:targets].each do |target|
        puts target
      end
    end
  end
end

main

```

</details>

### REST API

NOTES
- rate limitation があるので呼び出し過ぎに注意．

## Refs

https://github.com/technicalpickles/pager_duty-connection

https://hub.docker.com/_/ruby

https://developer.pagerduty.com/docs/ZG9jOjQ2NDA2-introduction

https://github.com/envato/pagerduty

→ こちらは Event API を呼ぶっぽい