reactio 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 36606269241ac620fd46cec36ca3cf045e3b8908
4
+ data.tar.gz: 85c450f2de769ae85546cb9031cedd4671cb90a8
5
+ SHA512:
6
+ metadata.gz: fe2eadf9a63c611c44cf8bd41665f20b706684f1c69e2d84ffacd777556d189f247c4b9c90f292f40c7580ac76676a94626756c6f32382df2b9236a8898c6855
7
+ data.tar.gz: 8e3e02366a1851008c23b279145b2b47c3fadabe19a4d3aa615007fc11756f295609da0645d8232e555005277f9ffa74d70caa5b7650b09fdf354cde2a103ef8
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /vendor/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ .rspec
11
+ /tmp/
12
+ *.bundle
13
+ *.so
14
+ *.o
15
+ *.a
16
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in reactio.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Hajime Sueyoshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,118 @@
1
+ # Reactio
2
+
3
+ Reactio API Client for ruby
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'reactio'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install reactio
20
+
21
+ ## Usage
22
+
23
+ ### include Reactio
24
+
25
+ ```ruby
26
+ require 'reactio'
27
+ include Reactio
28
+
29
+ reactio.create_incident('An Incident')
30
+ ```
31
+
32
+ And run:
33
+
34
+ $ REACTIO_API_KEY='YOUR_API_KEY' REACTIO_ORGANIZATION='YOUR_ORGANIZATION' bundle exec ruby create_incident.rb
35
+
36
+ ### Or instantiate Reactio::Service
37
+
38
+ ```ruby
39
+ require 'reactio'
40
+
41
+ reactio = Reactio::Service.new(
42
+ api_key: 'YOUR_API_KEY',
43
+ organization: 'YOUR_ORGANIZATION'
44
+ )
45
+ reactio.create_incident('An Incident')
46
+ ```
47
+
48
+ ### Reactio incidents
49
+
50
+ - インシデント作成(およびトピック登録)
51
+
52
+ ```ruby
53
+ require 'reactio'
54
+ include Reactio
55
+
56
+ reactio.create_incident(
57
+ 'サイト閲覧不可',
58
+ status: 'open',
59
+ detection: 'internal',
60
+ cause: 'over-capacity',
61
+ cause_supplement: 'Webサーバがアクセス過多でダウン',
62
+ point: 'application',
63
+ scale: 'point',
64
+ pend_text: 'Webサーバの再起動を行う',
65
+ topics: %w(原因調査 復旧作業),
66
+ notification_text: '至急対応をお願いします',
67
+ notification_call: false
68
+ )
69
+ ```
70
+
71
+ - 一斉通知
72
+
73
+ ```ruby
74
+ require 'reactio'
75
+ include Reactio
76
+
77
+ reactio.notify_incident(
78
+ 123,
79
+ notification_text: '至急対応をお願いします',
80
+ notification_call: true
81
+ )
82
+ ```
83
+
84
+ - インシデント一覧取得
85
+
86
+ ```ruby
87
+ require 'reactio'
88
+ include Reactio
89
+
90
+ list = reactio.list_incidents(
91
+ from: Time.now - (60 * 60 * 24 * 7),
92
+ to: Time.now,
93
+ status: 'pend',
94
+ page: 1,
95
+ per_page: 50
96
+ )
97
+
98
+ p list.first
99
+ #=> {:id=>1, :name=>"サイト閲覧不可", :manager=>nil, :status=>"pend", :detection=>"msp", :cause=>"over-capacity", :cause_supplement=>"Webサーバがアクセス過多でダウン", :point=>"middleware", :scale=>"whole", :pend_text=>"Webサーバの再起動を行う", :close_text=>"Webサーバのスケールアウトを行う", :closed_by=>nil, :closed_at=>nil, :pended_by=>nil, :pended_at=>nil, :created_by=>0, :created_at=>1430208000, :updated_by=>0, :updated_at=>1430208000}
100
+ ```
101
+
102
+ - インシデント取得
103
+
104
+ ```ruby
105
+ require 'reactio'
106
+ include Reactio
107
+
108
+ p reactio.describe_incident(123)
109
+ #=> {:id=>123, :name=>"サイト閲覧不可", :manager=>nil, :status=>"open", :detection=>"msp", :cause=>"over-capacity", :cause_supplement=>"Webサーバがアクセス過多でダウン", :point=>"middleware", :scale=>"whole", :pend_text=>"Webサーバの再起動を行う", :close_text=>"Webサーバのスケールアウトを行う", :closed_by=>nil, :closed_at=>nil, :pended_by=>nil, :pended_at=>nil, :created_by=>0, :created_at=>1430208000, :updated_by=>0, :updated_at=>1430208000, :topics=>[{:id=>1, :name=>"原因調査", :status=>"open", :color=>"#5661aa", :closed_by=>nil, :closed_at=>nil, :created_by=>0, :created_at=>1430208000, :updated_by=>0, :updated_at=>1430208000}, {:id=>2, :name=>"復旧作業", :status=>"open", :color=>"#077f40", :closed_by=>nil, :closed_at=>nil, :created_by=>0, :created_at=>1430208000, :updated_by=>0, :updated_at=>1430208000}], :files=>[{:name=>"障害報告書", :path=>"https://demo.reactio.jp/data/reactio-mvp/files/incident/1/_bYMRLTxj75lcXCWN0iaAZud2CuGqFFL/Screen_Shot.png"}], :users=>[{:id=>1}, {:id=>2}]}
110
+ ```
111
+
112
+ ## Contributing
113
+
114
+ 1. Fork it ( https://github.com/[my-github-username]/reactio/fork )
115
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
116
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
117
+ 4. Push to the branch (`git push origin my-new-feature`)
118
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,29 @@
1
+ require 'reactio/version'
2
+ require 'reactio/errors'
3
+ require 'reactio/utils'
4
+ require 'reactio/service'
5
+
6
+ module Reactio
7
+
8
+ def self.included(base)
9
+ base.class_eval do
10
+ include InstanceMethods
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+
16
+ def reactio
17
+ @reactio ||= create_service
18
+ end
19
+
20
+ private
21
+
22
+ def create_service
23
+ Service.new(
24
+ api_key: ENV['REACTIO_API_KEY'],
25
+ organization: ENV['REACTIO_ORGANIZATION']
26
+ )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ require 'faraday'
2
+ require 'reactio/api_endpoint'
3
+ require 'reactio/faraday_engine'
4
+
5
+ module Reactio
6
+ class APIClient
7
+
8
+ class << self
9
+
10
+ def build(api_key, organization)
11
+ raise ArgumentError, 'api_key is required' unless api_key
12
+ raise ArgumentError, 'organization is required' unless organization
13
+ http_client = build_http_client(
14
+ api_key,
15
+ APIEndpoint.new(organization)
16
+ )
17
+ new(http_client)
18
+ end
19
+
20
+ def build_http_client(api_key, api_endpoint)
21
+ Faraday.new(url: api_endpoint.base_url) do |faraday|
22
+ faraday.use :reactio, api_key
23
+ faraday.request :url_encoded
24
+ faraday.adapter Faraday.default_adapter
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(http_client)
30
+ @http = http_client
31
+ end
32
+
33
+ def request(method, path, env = {})
34
+ response = @http.send(method, path, env[:body])
35
+ handle_api_response(response)
36
+ end
37
+
38
+ private
39
+
40
+ def handle_api_response(res)
41
+ case res.status
42
+ when 200..299
43
+ res.body
44
+ when 401
45
+ raise Reactio::AuthenticationError, res.body.inspect
46
+ when 400..499
47
+ raise Reactio::BadRequest, res.body.inspect
48
+ when 500..599
49
+ raise Reactio::ServerError, res.body.inspect
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ require 'uri'
2
+
3
+ module Reactio
4
+ class APIEndpoint
5
+ DOMAIN = 'reactio.jp'.freeze
6
+
7
+ attr_reader :organization
8
+
9
+ def initialize(organization)
10
+ @organization = organization
11
+ @base_url = URI::HTTPS.build(host: "#{organization}.#{DOMAIN}")
12
+ end
13
+
14
+ def base_url
15
+ @base_url.to_s
16
+ end
17
+
18
+ def ==(other)
19
+ other.instance_of?(self.class) &&
20
+ self.organization == other.organization
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module Reactio
2
+ class BadRequest < StandardError; end
3
+ class AuthenticationError < StandardError; end
4
+ class ServerError < StandardError; end
5
+ end
@@ -0,0 +1,43 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Reactio
5
+ class FaradayEngine < Faraday::Middleware
6
+ USER_AGENT = "Reactio ruby v#{VERSION}".freeze
7
+ MIME_TYPE = 'application/json'.freeze
8
+
9
+ def initialize(app, api_key)
10
+ super(app)
11
+ @api_key = api_key
12
+ end
13
+
14
+ def call(request_env)
15
+ set_request_header(request_env)
16
+ encode_body(request_env)
17
+ @app.call(request_env).on_complete do |response_env|
18
+ decode_body(response_env)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def set_request_header(env)
25
+ env[:request_headers].merge!(
26
+ 'Accept' => MIME_TYPE,
27
+ 'Content-Type' => MIME_TYPE,
28
+ 'X-Api-Key' => @api_key,
29
+ 'User-Agent' => USER_AGENT
30
+ )
31
+ end
32
+
33
+ def encode_body(env)
34
+ env[:body] = JSON.dump(env[:body])
35
+ end
36
+
37
+ def decode_body(env)
38
+ env[:body] = JSON.parse(env[:body], symbolize_names: true)
39
+ end
40
+ end
41
+ end
42
+
43
+ Faraday::Middleware.register_middleware reactio: Reactio::FaradayEngine
@@ -0,0 +1,46 @@
1
+ require 'reactio/api_client'
2
+
3
+ module Reactio
4
+ class Service
5
+ include Utils
6
+
7
+ def initialize(options)
8
+ @api = APIClient.build(
9
+ options[:api_key],
10
+ options[:organization]
11
+ )
12
+ end
13
+
14
+ def create_incident(name, options = {})
15
+ payload = { name: name }.tap do |me|
16
+ me[:detection] = to_option_string(options.delete(:detection)) if options.key?(:detection)
17
+ me[:cause] = to_option_string(options.delete(:cause)) if options.key?(:cause)
18
+ me[:point] = to_option_string(options.delete(:point)) if options.key?(:point)
19
+ me[:scale] = to_option_string(options.delete(:scale)) if options.key?(:scale)
20
+ end
21
+ .merge!(options)
22
+ @api.request(:post, "/api/v1/incidents", body: payload)
23
+ end
24
+
25
+ def describe_incident(incident_id)
26
+ @api.request(:get, "/api/v1/incidents/#{incident_id}")
27
+ end
28
+
29
+ def list_incidents(options = {})
30
+ payload = {}.tap do |me|
31
+ me[:from] = options.delete(:from).to_i if options[:from]
32
+ me[:to] = options.delete(:to).to_i if options[:to]
33
+ me[:status] = options.delete(:status).to_s if options[:status]
34
+ end
35
+ .merge!(options)
36
+ @api.request(:get, "/api/v1/incidents", body: payload)
37
+ end
38
+
39
+ def notify_incident(incident_id, options = {})
40
+ @api.request(
41
+ :post, "/api/v1/notifications",
42
+ body: { incident_id: incident_id }.merge(options)
43
+ )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Reactio
2
+ module Utils
3
+ NIL_STRING = 'null'.freeze
4
+
5
+ def to_option_string(symbol)
6
+ return NIL_STRING if symbol.nil?
7
+ symbol.to_s.gsub('_', '-')
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Reactio
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'reactio/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reactio"
8
+ spec.version = Reactio::VERSION
9
+ spec.authors = ["Hajime Sueyoshi"]
10
+ spec.email = ["hajime.sueyoshi@gaiax.com"]
11
+ spec.summary = %q{Reactio API Client}
12
+ spec.description = %q{The official Reactio API Client for ruby.}
13
+ spec.homepage = "https://reactio.jp/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "faraday"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "webmock"
27
+ end
@@ -0,0 +1,59 @@
1
+ ---
2
+ :id: 1
3
+ :name: "サイト閲覧不可"
4
+ :manager:
5
+ :status: open
6
+ :detection: msp
7
+ :cause: over-capacity
8
+ :cause_supplement: Webサーバがアクセス過多でダウン
9
+ :point: middleware
10
+ :scale: whole
11
+ :pend_text: Webサーバの再起動を行う
12
+ :close_text: Webサーバのスケールアウトを行う
13
+ :closed_by:
14
+ :closed_at:
15
+ :pended_by:
16
+ :pended_at:
17
+ :created_by: 0
18
+ :created_at: 1430208000
19
+ :updated_by: 0
20
+ :updated_at: 1430208000
21
+ :topics:
22
+ - :id: 1
23
+ :name: "原因調査"
24
+ :status: open
25
+ :color: "#5661aa"
26
+ :closed_by:
27
+ :closed_at:
28
+ :created_by: 0
29
+ :created_at: 1430208000
30
+ :updated_by: 0
31
+ :updated_at: 1430208000
32
+ - :id: 2
33
+ :name: "復旧作業"
34
+ :status: open
35
+ :color: "#077f40"
36
+ :closed_by:
37
+ :closed_at:
38
+ :created_by: 0
39
+ :created_at: 1430208000
40
+ :updated_by: 0
41
+ :updated_at: 1430208000
42
+ :notifications:
43
+ :id: 1
44
+ :notification_text: "お疲れ様です。Webサーバで障害が発生しました。至急対応をお願い致します。"
45
+ :notification_call: true
46
+ :notificated_for:
47
+ :phone:
48
+ - '1'
49
+ - '3'
50
+ :email1:
51
+ - '1'
52
+ - '2'
53
+ - '3'
54
+ :email2:
55
+ - '1'
56
+ - '2'
57
+ :imkayac:
58
+ - '1'
59
+ :created_at: 1430208000