Full Code of ryotarai/waker for AI

master 24b15020a036 cached
270 files
156.8 KB
50.7k tokens
338 symbols
1 requests
Download .txt
Showing preview only (212K chars total). Download the full file or copy to clipboard to get everything.
Repository: ryotarai/waker
Branch: master
Commit: 24b15020a036
Files: 270
Total size: 156.8 KB

Directory structure:
gitextract_ao1b4jon/

├── .dockerignore
├── .github/
│   └── workflows/
│       └── rails.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── Dockerfile
├── Gemfile
├── LICENSE.txt
├── Procfile
├── Procfile.docker
├── README.md
├── Rakefile
├── app/
│   ├── assets/
│   │   ├── images/
│   │   │   └── .keep
│   │   ├── javascripts/
│   │   │   ├── application.js
│   │   │   ├── comments.coffee
│   │   │   ├── escalation_series.coffee
│   │   │   ├── escalations.coffee
│   │   │   ├── home.coffee
│   │   │   ├── incident_events.coffee
│   │   │   ├── incidents.coffee
│   │   │   ├── maintenances.coffee
│   │   │   ├── notifier_providers.coffee
│   │   │   ├── notifiers.coffee
│   │   │   ├── sessions.coffee
│   │   │   ├── slack.coffee
│   │   │   ├── topics.coffee
│   │   │   └── users.coffee
│   │   └── stylesheets/
│   │       ├── application.css
│   │       ├── comments.scss
│   │       ├── escalation_series.scss
│   │       ├── escalations.scss
│   │       ├── home.scss
│   │       ├── incident_events.scss
│   │       ├── incidents.scss
│   │       ├── maintenances.scss
│   │       ├── notifier_providers.scss
│   │       ├── notifiers.scss
│   │       ├── scaffolds.scss
│   │       ├── sessions.scss
│   │       ├── slack.scss
│   │       ├── topics.scss
│   │       └── users.scss
│   ├── controllers/
│   │   ├── application_controller.rb
│   │   ├── comments_controller.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── escalation_series_controller.rb
│   │   ├── escalations_controller.rb
│   │   ├── home_controller.rb
│   │   ├── incident_events_controller.rb
│   │   ├── incidents_controller.rb
│   │   ├── maintenances_controller.rb
│   │   ├── notifier_providers_controller.rb
│   │   ├── notifiers_controller.rb
│   │   ├── sessions_controller.rb
│   │   ├── slack_controller.rb
│   │   ├── topics_controller.rb
│   │   └── users_controller.rb
│   ├── helpers/
│   │   ├── application_helper.rb
│   │   ├── comments_helper.rb
│   │   ├── escalation_series_helper.rb
│   │   ├── escalations_helper.rb
│   │   ├── home_helper.rb
│   │   ├── incident_events_helper.rb
│   │   ├── incidents_helper.rb
│   │   ├── maintenances_helper.rb
│   │   ├── notifier_providers_helper.rb
│   │   ├── notifiers_helper.rb
│   │   ├── sessions_helper.rb
│   │   ├── slack_helper.rb
│   │   ├── topics_helper.rb
│   │   └── users_helper.rb
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   ├── .keep
│   │   ├── application_record.rb
│   │   ├── comment.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── escalation.rb
│   │   ├── escalation_series.rb
│   │   ├── escalation_update_worker.rb
│   │   ├── escalation_worker.rb
│   │   ├── incident.rb
│   │   ├── incident_event.rb
│   │   ├── maintenance.rb
│   │   ├── notification_worker.rb
│   │   ├── notifier.rb
│   │   ├── notifier_provider.rb
│   │   ├── topic.rb
│   │   └── user.rb
│   └── views/
│       ├── comments/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── escalation_series/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── escalations/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── home/
│       │   └── index.html.erb
│       ├── incident_events/
│       │   └── twilio.html.erb
│       ├── incidents/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── layouts/
│       │   └── application.html.erb
│       ├── maintenances/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── notifier_providers/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── hipchat/
│       │   │   ├── acknowledged.text.erb
│       │   │   ├── escalated.text.erb
│       │   │   ├── opened.text.erb
│       │   │   └── resolved.text.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── mailgun/
│       │   │   ├── default.html.erb
│       │   │   └── default.text.erb
│       │   ├── new.html.erb
│       │   ├── rails_logger/
│       │   │   └── default.text.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── notifiers/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── sessions/
│       │   └── create.html.erb
│       ├── slack/
│       │   └── interactive.html.erb
│       ├── topics/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       └── users/
│           ├── _form.html.erb
│           ├── edit.html.erb
│           ├── index.html.erb
│           ├── index.json.jbuilder
│           ├── new.html.erb
│           ├── show.html.erb
│           └── show.json.jbuilder
├── bin/
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── spring
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── initializers/
│   │   ├── assets.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── cookies_serializer.rb
│   │   ├── field_with_errors.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── omniauth.rb
│   │   ├── session_store.rb
│   │   ├── sidekiq.rb
│   │   ├── url_options.rb
│   │   └── wrap_parameters.rb
│   ├── locales/
│   │   └── en.yml
│   ├── routes.rb
│   └── secrets.yml
├── config.ru
├── db/
│   ├── migrate/
│   │   ├── 20150120134616_create_topics.rb
│   │   ├── 20150120134747_create_users.rb
│   │   ├── 20150120134905_create_notifiers.rb
│   │   ├── 20150120135017_create_shifts.rb
│   │   ├── 20150120135123_create_escalations.rb
│   │   ├── 20150120135244_create_escalation_series.rb
│   │   ├── 20150120135351_add_escalation_series_to_escalation.rb
│   │   ├── 20150120141627_rename_type_with_kind_of_topic.rb
│   │   ├── 20150120142452_create_incidents.rb
│   │   ├── 20150120151642_add_escalation_series_to_topic.rb
│   │   ├── 20150120154438_add_name_to_shift.rb
│   │   ├── 20150121150043_add_user_to_notifier.rb
│   │   ├── 20150121150857_rename_type_with_kind_of_notifier.rb
│   │   ├── 20150123132415_remove_shift.rb
│   │   ├── 20150123150518_add_status_to_incident.rb
│   │   ├── 20150123150947_create_incident_events.rb
│   │   ├── 20150125050529_create_notifier_providers.rb
│   │   ├── 20150125050556_add_provider_to_notifier.rb
│   │   ├── 20150125101901_remove_kind_from_notifier.rb
│   │   ├── 20150127142530_remove_user_by_from_incident_event.rb
│   │   ├── 20150127152127_add_info_to_incident_event.rb
│   │   ├── 20150128064248_add_email_to_user.rb
│   │   ├── 20150131120557_add_enable_to_topic.rb
│   │   ├── 20150131121143_set_default_of_enable_of_topic_true.rb
│   │   ├── 20150131122151_rename_enable_with_enabled_of_topic.rb
│   │   ├── 20150201033946_add_login_token_to_user.rb
│   │   ├── 20150202144538_add_token_to_user.rb
│   │   ├── 20150202151740_add_refresh_token_to_user.rb
│   │   ├── 20150202152015_add_settings_to_escalation_series.rb
│   │   ├── 20150202155726_add_token_expires_at_to_user.rb
│   │   ├── 20150203010332_remove_escalation_series_from_escalation_series.rb
│   │   ├── 20150203010417_add_settings_to_escalation_series_again.rb
│   │   ├── 20150207164010_add_topic_to_notifier.rb
│   │   ├── 20150218071007_add_enable_to_notifier.rb
│   │   ├── 20151117011141_add_active_to_user.rb
│   │   ├── 20151117013824_add_provider_and_uid_to_user.rb
│   │   ├── 20151118061253_add_credentials_to_user.rb
│   │   ├── 20151118061938_delete_token_from_user.rb
│   │   ├── 20160210010310_create_maintenances.rb
│   │   ├── 20160907123728_create_comments.rb
│   │   ├── 20160914063913_add_comment_index.rb
│   │   └── 20161207045554_add_filter_to_maintenance.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker/
│   └── puma.rb
├── docker-compose.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   └── tasks/
│       └── .keep
├── log/
│   └── .keep
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   └── robots.txt
├── script/
│   └── update-escalations-from-google-calendar
├── spec/
│   ├── controllers/
│   │   └── slack_controller_spec.rb
│   ├── factories/
│   │   ├── escalation.rb
│   │   ├── escalation_series.rb
│   │   ├── incident.rb
│   │   ├── incident_events.rb
│   │   ├── maintenances.rb
│   │   ├── notifier.rb
│   │   ├── notifier_provider.rb
│   │   ├── topic.rb
│   │   └── user.rb
│   ├── helpers/
│   │   └── slack_helper_spec.rb
│   ├── models/
│   │   ├── comment_spec.rb
│   │   ├── escalation_series_spec.rb
│   │   ├── incident_spec.rb
│   │   └── maintenance_spec.rb
│   ├── rails_helper.rb
│   ├── requests/
│   │   ├── incident_envets_spec.rb
│   │   ├── maintenances_spec.rb
│   │   └── topics_spec.rb
│   ├── spec_helper.rb
│   ├── support/
│   │   ├── factory_girl.rb
│   │   └── sidekiq.rb
│   └── views/
│       └── slack/
│           └── interactive.html.erb_spec.rb
└── vendor/
    └── assets/
        ├── javascripts/
        │   └── .keep
        └── stylesheets/
            └── .keep

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
logs/*
doc/*
vendor/*
coverage/*
tmp/*


================================================
FILE: .github/workflows/rails.yml
================================================
name: Rails

on: [push]

jobs:

  build:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    container:
      image: ruby:2.6.5
      env:
        MYSQL_HOST: mysql
    steps:
    - uses: actions/checkout@v1
    - name: Setup YARN and NodeJS
      run: |
        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
        curl -sL https://deb.nodesource.com/setup_12.x | bash -
        apt-get install -y yarn nodejs
    - name: Build and setup
      run: |
        gem install bundler --no-document
        bundle install --jobs 4 --retry 3 --deployment
        bundle exec rails yarn:install db:setup assets:precompile
        bundle exec rake
      env:
        RAILS_ENV: "test"


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal

# Ignore all logfiles and tempfiles.
/log/*
!/log/.keep
/tmp
*.rdb
.env
coverage


================================================
FILE: .rspec
================================================
--color
--require spec_helper


================================================
FILE: .rubocop.yml
================================================
AllCops:
  TargetRubyVersion: 2.4
  DisplayCopNames: true
  DisabledByDefault: true
  Exclude:
    - 'db/**/*'
    - 'vendor/**/*'

Rails:
  Enabled: true

Rails/ActionFilter:
  EnforcedStyle: action
  Enabled: true

Style/HashSyntax:
  Enabled: true

Style/MethodDefParentheses:
  Enabled: true

Style/Encoding:
  Enabled: true

Style/For:
  EnforcedStyle: each
  Enabled: true

Style/FrozenStringLiteralComment:
  EnforcedStyle: never
  Enabled: true

Style/NumericLiteralPrefix:
  EnforcedOctalStyle: zero_only
  Enabled: true

Style/StabbyLambdaParentheses:
  EnforcedStyle: require_parentheses
  Enabled: true

Layout/EmptyLines:
  Enabled: true

Layout/TrailingBlankLines:
  Enabled: true

Layout/TrailingWhitespace:
  Enabled: true

Layout/AccessModifierIndentation:
  EnforcedStyle: indent
  Enabled: true

Layout/CaseIndentation:
  EnforcedStyle: end
  Enabled: true

Layout/MultilineHashBraceLayout:
  EnforcedStyle: symmetrical
  Enabled: true


================================================
FILE: Dockerfile
================================================
FROM ruby:2.6.5

RUN apt update -qqy && apt -qqy install nodejs

WORKDIR /tmp
ADD Gemfile* /tmp/

RUN gem install bundler:2.1.4
RUN bundle install --deployment -j4 --without development test

ADD . /app
WORKDIR /app
RUN cp -a /tmp/vendor/bundle /app/vendor/bundle && \
    bundle exec rake assets:precompile
CMD ["bundle", "exec", "foreman", "start", "-f", "Procfile.docker"]


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '5.2.4.1'

# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc'

# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Unicorn as the app server
# gem 'unicorn'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

gem 'sidekiq', '~> 3.3.0'
gem 'sinatra'

gem 'faraday', '~> 0.15.4'
gem 'holiday_jp'
gem 'hipchat'
gem 'kaminari'

gem 'puma'
gem 'rack-health'
gem 'omniauth-google-oauth2', '~> 0.6'
gem 'retriable', '3.0.1'
gem 'twilio-ruby'
gem 'mysql2'

gem 'google-api-client', '~> 0.7.1'

gem 'foreman'

group :development do
  gem 'web-console'
end

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'

  # Access an IRB console on exception pages or by using <%= console %> in views

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'

  gem 'rspec-rails'

  gem 'database_rewinder'

  gem 'pry-byebug'
  gem 'dotenv-rails'
  gem 'factory_bot_rails'
  gem 'rubocop', require: false
end

group :test do
  gem 'simplecov', require: false
end

gem 'dogapi'
gem 'aws-sdk'
gem 'rails_autolink'


================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2014-2015 Ryota Arai

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Procfile
================================================
web: bin/rails s
worker: bundle exec sidekiq
update_escalations: while true; do bundle exec rails runner 'EscalationUpdateWorker.perform_async()'; sleep 60; done


================================================
FILE: Procfile.docker
================================================
web: puma -C docker/puma.rb
worker: bundle exec sidekiq
update_escalations: while true; do bundle exec rails runner 'EscalationUpdateWorker.perform_async()'; sleep 60; done


================================================
FILE: README.md
================================================
# Waker [![Build Status](https://travis-ci.org/ryotarai/waker.svg?branch=master)](https://travis-ci.org/ryotarai/waker)

Alert Escalation System

![](https://raw.githubusercontent.com/ryotarai/waker/master/doc/incidents.png)

## Overview

![](https://raw.githubusercontent.com/ryotarai/waker/master/doc/overview.png)

![](https://raw.githubusercontent.com/ryotarai/waker/master/doc/escalation.png)

## Tutorial

### 1. (Optional) Configure auth provider

You can use external auth provider **optionally**. Currently, Google Auth is only supported (Patches are welcome :) )

```
$ echo 'GOOGLE_CLIENT_ID=...' >> .env
$ echo 'GOOGLE_CLIENT_SECRET=...' >> .env
$ echo 'GOOGLE_DOMAIN=...' >> .env # If you restrict to use Google Apps domain
```

### 2. Start the server

```
$ bundle install
$ foreman start
```

It starts an application server and a Sidekiq worker.

### 3. (If you uses auth provider) Log in

Visit [http://localhost:3000](http://localhost:3000) and log in with your credentials.
A new user account is automatically created and suspended by default. You can activate a user from [http://localhost:3000/users](http://localhost:3000/users) but you have to activate it from `rails console` because you are the first user:

```
$ bundle exec rails c
> User.first.update!(active: true)
```

### 4. Create users

Visit [http://localhost:3000/users/new](http://localhost:3000/users/new) and create new users.

### 5. Create a notifier provider

Visit [http://localhost:3000/notifier_providers/new](http://localhost:3000/notifier_providers/new) and create a notifier provider. See [Notifier Providers](https://github.com/ryotarai/waker#notifier-providers) section for detailed information.

### 6. Create a notifier

Visit [http://localhost:3000/notifiers/new](http://localhost:3000/notifiers/new) and create a notifier. See [Notifier](https://github.com/ryotarai/waker#notifiers) section for detailed information.

### 7. Create an escalation series

Visit [http://localhost:3000/escalation_series/new](http://localhost:3000/escalation_series/new) and create a escalation series. Escalation series is a series of escalations.

### 8. Create escalations

Visit [http://localhost:3000/escalations/new](http://localhost:3000/escalations/new) and create escalations.

- `Escalate to`: Who gets escalated incidents
- `Escalate after sec`: Seconds to escalate incidents since the incidents created

### 9. Create a topic

Visit [http://localhost:3000/topics/new](http://localhost:3000/topics/new) and create topics.

### 10. Send alerts to the topic

Suppoted alerts generaters are below:

- Mailgun ( `http://localhost:3000/topics/1/mailgun` )
- Mackerel ( `http://localhost:3000/topics/1/mackerel` )
- Alertmanager ( `http://localhost:3000/topics/1/alertmanager` )
- Slack( `http://localhost:3000/topics/1/slack` )

If you want to use Mailgun, you can configure Mailgun route setting with Mailgun endpoint you can see in [http://localhost:3000/topics/1/mailgun](http://localhost:3000/topics/1/mailgun)
## Configuration

### Notifier Providers

#### HipChat

- `api_token`
- `api_version`: `v1` or `v2`

#### Twilio

- `account_sid`
- `auth_token`
- `from`: Phone number

#### Mailgun

- `api_key`
- `from`: Email address

### Notifiers

#### Common fields

These are supported by all notifier provider

```
or_conditions:
- japanese_weekday: true
  not_between: 9:30+0900-18:30+0900
- not_japanese_weekday: true
```

#### HipChat

- `room`: Room name or ID

#### Twilio

- `to`: Phone number

#### Mailgun

- `to`: Email address


================================================
FILE: Rakefile
================================================
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

Rails.application.load_tasks


================================================
FILE: app/assets/images/.keep
================================================


================================================
FILE: app/assets/javascripts/application.js
================================================
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .


================================================
FILE: app/assets/javascripts/comments.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/escalation_series.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/escalations.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/home.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/incident_events.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/incidents.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/maintenances.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/notifier_providers.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/notifiers.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/sessions.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/slack.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/topics.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/javascripts/users.coffee
================================================
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/


================================================
FILE: app/assets/stylesheets/application.css
================================================
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any styles
 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
 * file per style scope.
 *
 *= require_tree .
 *= require_self
 */


================================================
FILE: app/assets/stylesheets/comments.scss
================================================
// Place all the styles related to the comments controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/escalation_series.scss
================================================
// Place all the styles related to the EscalationSeries controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/escalations.scss
================================================
// Place all the styles related to the escalations controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/home.scss
================================================
// Place all the styles related to the home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/incident_events.scss
================================================
// Place all the styles related to the incident_events controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/incidents.scss
================================================
// Place all the styles related to the incidents controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/maintenances.scss
================================================
// Place all the styles related to the maintenances controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/notifier_providers.scss
================================================
// Place all the styles related to the NotifierProviders controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/notifiers.scss
================================================
// Place all the styles related to the notifiers controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/scaffolds.scss
================================================
body {
  background-color: #fff;
  color: #333;
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: 13px;
  line-height: 18px;
}

p, ol, ul, td {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: 13px;
  line-height: 18px;
}

pre {
  background-color: #eee;
  padding: 10px;
  font-size: 11px;
}

a {
  color: #000;
  &:visited {
    color: #666;
  }
  &:hover {
    color: #fff;
    background-color: #000;
  }
}

div {
  &.field, &.actions {
    margin-bottom: 10px;
  }
}

#notice {
  color: green;
}

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

#error_explanation {
  width: 450px;
  border: 2px solid red;
  padding: 7px;
  padding-bottom: 0;
  margin-bottom: 20px;
  background-color: #f0f0f0;
  h2 {
    text-align: left;
    font-weight: bold;
    padding: 5px 5px 5px 15px;
    font-size: 12px;
    margin: -7px;
    margin-bottom: 0px;
    background-color: #c00;
    color: #fff;
  }
  ul li {
    font-size: 12px;
    list-style: square;
  }
}


================================================
FILE: app/assets/stylesheets/sessions.scss
================================================
// Place all the styles related to the sessions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/slack.scss
================================================
// Place all the styles related to the slack controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/topics.scss
================================================
// Place all the styles related to the topics controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/assets/stylesheets/users.scss
================================================
// Place all the styles related to the users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/


================================================
FILE: app/controllers/application_controller.rb
================================================
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :null_session
  helper_method :current_user

  if ENV["GOOGLE_CLIENT_ID"]
    before_action :login_required
  end

  def login_required
    unless current_user
      session[:user_id] = nil
      redirect_to '/auth/google_oauth2_with_calendar'
      return
    end

    unless current_user.active
      render text: "You are not activated yet. Please ask administrator to activate you"
      return
    end
  end

  private
  def current_user=(user)
    session[:user_id] = user.id
  end

  def current_user
    if user_id = session[:user_id]
      User.find(user_id)
    elsif login_token = request.headers['X-Login-Token']
      User.find_by(login_token: login_token)
    end
  end
end


================================================
FILE: app/controllers/comments_controller.rb
================================================
class CommentsController < ApplicationController
  before_action :set_comment, only: [:show, :edit, :update, :destroy]
  before_action :set_incident

  # GET /comments
  # GET /comments.json
  def index
    @comments = Comment.where(incident: @incident)
  end

  # GET /comments/1
  # GET /comments/1.json
  def show
  end

  # GET /comments/new
  def new
    @comment = Comment.new
  end

  # GET /comments/1/edit
  def edit
  end

  # POST /comments
  # POST /comments.json
  def create
    p comment_params
    @comment = Comment.new(comment_params)

    respond_to do |format|
      if @comment.save
        format.html { redirect_to [@incident, @comment], notice: 'Comment was successfully created.' }
        format.json { render :show, status: :created, location: [@incident, @comment] }
      else
        format.html { render :new }
        format.json { render json: @comment.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /comments/1
  # PATCH/PUT /comments/1.json
  def update
    respond_to do |format|
      if @comment.update(comment_params)
        format.html { redirect_to [@incident, @comment], notice: 'Comment was successfully updated.' }
        format.json { render :show, status: :ok, location: [@incident, @comment] }
      else
        format.html { render :edit }
        format.json { render json: @comment.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /comments/1
  # DELETE /comments/1.json
  def destroy
    @comment.destroy
    respond_to do |format|
      format.html { redirect_to incident_comments_url, notice: 'Comment was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_comment
      @comment = Comment.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def comment_params
      params.require(:comment).permit(:incident_id, :user_id, :comment)
    end

    def set_incident
      @incident = Incident.find(params[:incident_id])
    end
end


================================================
FILE: app/controllers/concerns/.keep
================================================


================================================
FILE: app/controllers/escalation_series_controller.rb
================================================
class EscalationSeriesController < ApplicationController
  before_action :set_escalation_series, only: [:show, :edit, :update, :destroy, :update_escalations]

  # GET /escalation_series
  # GET /escalation_series.json
  def index
    @escalation_series = EscalationSeries.all
  end

  # GET /escalation_series/1
  # GET /escalation_series/1.json
  def show
  end

  # GET /escalation_series/new
  def new
    @escalation_series = EscalationSeries.new
  end

  # GET /escalation_series/1/edit
  def edit
  end

  # POST /escalation_series
  # POST /escalation_series.json
  def create
    @escalation_series = EscalationSeries.new(escalation_series_params)

    respond_to do |format|
      if @escalation_series.save
        format.html { redirect_to @escalation_series, notice: 'Escalation series was successfully created.' }
        format.json { render :show, status: :created, location: @escalation_series }
      else
        format.html { render :new }
        format.json { render json: @escalation_series.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /escalation_series/1
  # PATCH/PUT /escalation_series/1.json
  def update
    respond_to do |format|
      if @escalation_series.update(escalation_series_params)
        format.html { redirect_to @escalation_series, notice: 'Escalation series was successfully updated.' }
        format.json { render :show, status: :ok, location: @escalation_series }
      else
        format.html { render :edit }
        format.json { render json: @escalation_series.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /escalation_series/1
  # DELETE /escalation_series/1.json
  def destroy
    @escalation_series.destroy
    respond_to do |format|
      format.html { redirect_to escalation_series_index_url, notice: 'Escalation series was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  def update_escalations
    @escalation_series.update_escalations!
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_escalation_series
      @escalation_series = EscalationSeries.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def escalation_series_params
      params.require(:escalation_series).permit(:name, :settings).tap do |v|
        v[:settings] = YAML.load(v[:settings])
      end
    end
end


================================================
FILE: app/controllers/escalations_controller.rb
================================================
class EscalationsController < ApplicationController
  before_action :set_escalation, only: [:show, :edit, :update, :destroy]

  # GET /escalations
  # GET /escalations.json
  def index
    @escalations = Escalation.all.order('escalate_after_sec')
  end

  # GET /escalations/1
  # GET /escalations/1.json
  def show
  end

  # GET /escalations/new
  def new
    @escalation = Escalation.new
  end

  # GET /escalations/1/edit
  def edit
  end

  # POST /escalations
  # POST /escalations.json
  def create
    @escalation = Escalation.new(escalation_params)

    respond_to do |format|
      if @escalation.save
        format.html { redirect_to @escalation, notice: 'Escalation was successfully created.' }
        format.json { render :show, status: :created, location: @escalation }
      else
        format.html { render :new }
        format.json { render json: @escalation.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /escalations/1
  # PATCH/PUT /escalations/1.json
  def update
    respond_to do |format|
      if @escalation.update(escalation_params)
        format.html { redirect_to @escalation, notice: 'Escalation was successfully updated.' }
        format.json { render :show, status: :ok, location: @escalation }
      else
        format.html { render :edit }
        format.json { render json: @escalation.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /escalations/1
  # DELETE /escalations/1.json
  def destroy
    @escalation.destroy
    respond_to do |format|
      format.html { redirect_to escalations_url, notice: 'Escalation was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_escalation
      @escalation = Escalation.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def escalation_params
      params.require(:escalation).permit(:escalate_to_id, :escalate_after_sec, :escalation_series_id)
    end
end


================================================
FILE: app/controllers/home_controller.rb
================================================
class HomeController < ApplicationController
  def index
    redirect_to incidents_path
  end
end


================================================
FILE: app/controllers/incident_events_controller.rb
================================================
require 'securerandom'

class IncidentEventsController < ApplicationController
  skip_before_action :login_required, only: [:twilio], raise: false

  def twilio
    @event = IncidentEvent.find(params[:id])
    language = ENV['TWILIO_LANGUAGE'] || 'en-US'
    resp = nil

    if params[:Digits]
      case params[:Digits]
      when '1'
        @event.incident.acknowledge! rescue nil
      when '2'
        @event.incident.resolve! rescue nil
      end
      resp = Twilio::TwiML::VoiceResponse.new do |r|
        r.say message: @event.incident.status, voice: 'alice', language: language
        r.hangup
      end
    else
      resp = Twilio::TwiML::VoiceResponse.new do |r|
        r.gather timeout: 10, numDigits: 1 do |g|
          g.say message: "This is Waker alert.", voice: 'alice', language: language
          g.say message: @event.incident.subject, voice: 'alice', language: language
          g.say message: "To acknowledge, press 1.", voice: 'alice', language: language
          g.say message: "To resolve, press 2.", voice: 'alice', language: language
        end
      end
    end

    render xml: resp.to_s
  end
end


================================================
FILE: app/controllers/incidents_controller.rb
================================================
class IncidentsController < ApplicationController
  before_action :set_incidents, only: [:index, :bulk_acknowledge, :bulk_resolve]
  before_action :set_incident, only: [:show, :edit, :update, :destroy, :acknowledge, :resolve]
  before_action :ensure_hash, only: [:acknowledge, :resolve]
  skip_before_action :login_required, only: [:acknowledge, :resolve], raise: false

  # GET /incidents
  # GET /incidents.json
  def index
    @page = (params[:page] || 1).to_i
    @incidents = @incidents.order('id DESC').page(@page).per(25)
  end

  # GET /incidents/1
  # GET /incidents/1.json
  def show
  end

  # GET /incidents/new
  def new
    @incident = Incident.new
  end

  # GET /incidents/1/edit
  def edit
  end

  # POST /incidents
  # POST /incidents.json
  def create
    @incident = Incident.new(incident_params)

    respond_to do |format|
      if @incident.save
        format.html { redirect_to @incident, notice: 'Incident was successfully created.' }
        format.json { render :show, status: :created, location: @incident }
      else
        format.html { render :new }
        format.json { render json: @incident.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /incidents/1
  # PATCH/PUT /incidents/1.json
  def update
    respond_to do |format|
      if @incident.update(incident_params)
        format.html { redirect_to @incident, notice: 'Incident was successfully updated.' }
        format.json { render :show, status: :ok, location: @incident }
      else
        format.html { render :edit }
        format.json { render json: @incident.errors, status: :unprocessable_entity }
      end
    end
  end

  def bulk_acknowledge
    @incidents.opened.update_all(status: Incident.statuses[:acknowledged])
    respond_to do |format|
      format.html { redirect_to incidents_url, notice: 'Incidents were successfully acknowledged.' }
      format.json { head :no_content }
    end
  end

  def bulk_resolve
    @incidents.update_all(status: Incident.statuses[:resolved])
    respond_to do |format|
      format.html { redirect_to incidents_url, notice: 'Incidents were successfully resolved.' }
      format.json { head :no_content }
    end
  end

  # DELETE /incidents/1
  # DELETE /incidents/1.json
  def destroy
    @incident.destroy
    respond_to do |format|
      format.html { redirect_to incidents_url, notice: 'Incident was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  def acknowledge
    @incident.acknowledge!
    respond_to do |format|
      if current_user
        format.html { redirect_to incidents_url, notice: 'Incident was successfully acknowledged.' }
      else
        format.html { render text: "Acknowledged" }
      end
      format.json { render json: {status: 'ok'} }
    end
  end

  def resolve
    @incident.resolve!
    respond_to do |format|
      if current_user
        format.html { redirect_to incidents_url, notice: 'Incident was successfully resolved.' }
      else
        format.html { render text: "Resolved" }
      end
      format.json { render json: {status: 'ok'} }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_incident
      @incident = Incident.find(params[:id])
    end

    def set_incidents
      set_visible_statuses
      set_visible_topic

      @incidents = Incident.all

      if @visible_statuses
        @incidents = @incidents.where(status: @visible_statuses)
      end
      if @visible_topic
        @incidents = @incidents.where(topic: @visible_topic)
      end
    end

    def set_visible_statuses
      @visible_statuses = session[:incidents_statuses]

      # for transition from rev 0a2dd42 or earlier
      @visible_statuses = nil if @visible_statuses.try(:empty?)

      if params[:statuses]
        if params[:statuses] == ''
          @visible_statuses = nil # all
        else
          @visible_statuses = params[:statuses].split(',').map(&:to_i)
        end
        session[:incidents_statuses] = @visible_statuses
      end
    end

    def set_visible_topic
      @visible_topic = session[:incidents_topic]

      # for transition from rev 0a2dd42 or earlier
      @visible_topic = nil if @visible_topic == 'all'

      if params[:topic]
        if params[:topic] == 'all'
          @visible_topic = nil # all
        else
          @visible_topic = params[:topic].to_i
        end
        session[:incidents_topic] = @visible_topic
      end
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def incident_params
      params.require(:incident).permit(:subject, :description, :topic_id, :occured_at)
    end

    def ensure_hash
      unless params[:hash] == @incident.confirmation_hash
        render text: "Wrong hash", status: 403
      end
    end
end


================================================
FILE: app/controllers/maintenances_controller.rb
================================================
class MaintenancesController < ApplicationController
  before_action :set_maintenance, only: [:show, :edit, :update, :destroy]

  # GET /maintenances
  # GET /maintenances.json
  def index
    @maintenances = Maintenance.not_expired
  end

  # GET /maintenances/1
  # GET /maintenances/1.json
  def show
  end

  # GET /maintenances/new
  def new
    now = Time.now
    @maintenance = Maintenance.new(
      start_time: now,
      end_time: now + 60*60
    )
  end

  # GET /maintenances/1/edit
  def edit
  end

  # POST /maintenances
  # POST /maintenances.json
  def create
    @maintenance = Maintenance.new(maintenance_params)

    respond_to do |format|
      if @maintenance.save
        format.html { redirect_to @maintenance, notice: 'Maintenance was successfully created.' }
        format.json { render :show, status: :created, location: @maintenance }
      else
        format.html { render :new }
        format.json { render json: @maintenance.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /maintenances/1
  # PATCH/PUT /maintenances/1.json
  def update
    respond_to do |format|
      if @maintenance.update(maintenance_params)
        format.html { redirect_to @maintenance, notice: 'Maintenance was successfully updated.' }
        format.json { render :show, status: :ok, location: @maintenance }
      else
        format.html { render :edit }
        format.json { render json: @maintenance.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /maintenances/1
  # DELETE /maintenances/1.json
  def destroy
    @maintenance.destroy
    respond_to do |format|
      format.html { redirect_to maintenances_url, notice: 'Maintenance was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_maintenance
      @maintenance = Maintenance.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def maintenance_params
      params.require(:maintenance).permit(:topic_id, :start_time, :end_time, :filter)
    end
end


================================================
FILE: app/controllers/notifier_providers_controller.rb
================================================
class NotifierProvidersController < ApplicationController
  before_action :set_notifier_provider, only: [:show, :edit, :update, :destroy]

  # GET /notifier_providers
  # GET /notifier_providers.json
  def index
    @notifier_providers = NotifierProvider.all
  end

  # GET /notifier_providers/1
  # GET /notifier_providers/1.json
  def show
  end

  # GET /notifier_providers/new
  def new
    @notifier_provider = NotifierProvider.new
  end

  # GET /notifier_providers/1/edit
  def edit
  end

  # POST /notifier_providers
  # POST /notifier_providers.json
  def create
    @notifier_provider = NotifierProvider.new(notifier_provider_params)

    respond_to do |format|
      if @notifier_provider.save
        format.html { redirect_to @notifier_provider, notice: 'Notifier provider was successfully created.' }
        format.json { render :show, status: :created, location: @notifier_provider }
      else
        format.html { render :new }
        format.json { render json: @notifier_provider.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /notifier_providers/1
  # PATCH/PUT /notifier_providers/1.json
  def update
    respond_to do |format|
      if @notifier_provider.update(notifier_provider_params)
        format.html { redirect_to @notifier_provider, notice: 'Notifier provider was successfully updated.' }
        format.json { render :show, status: :ok, location: @notifier_provider }
      else
        format.html { render :edit }
        format.json { render json: @notifier_provider.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /notifier_providers/1
  # DELETE /notifier_providers/1.json
  def destroy
    @notifier_provider.destroy
    respond_to do |format|
      format.html { redirect_to notifier_providers_url, notice: 'Notifier provider was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_notifier_provider
      @notifier_provider = NotifierProvider.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def notifier_provider_params
      params.require(:notifier_provider).permit(:name, :kind, :settings).tap do |v|
        v[:settings] = YAML.load(v[:settings])
      end
    end
end


================================================
FILE: app/controllers/notifiers_controller.rb
================================================
class NotifiersController < ApplicationController
  before_action :set_notifier, only: [:show, :edit, :update, :destroy]

  # GET /notifiers
  # GET /notifiers.json
  def index
    @notifiers = Notifier.all
  end

  # GET /notifiers/1
  # GET /notifiers/1.json
  def show
  end

  # GET /notifiers/new
  def new
    @notifier = Notifier.new
  end

  # GET /notifiers/1/edit
  def edit
  end

  # POST /notifiers
  # POST /notifiers.json
  def create
    @notifier = Notifier.new(notifier_params)

    respond_to do |format|
      if @notifier.save
        format.html { redirect_to @notifier, notice: 'Notifier was successfully created.' }
        format.json { render :show, status: :created, location: @notifier }
      else
        format.html { render :new }
        format.json { render json: @notifier.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /notifiers/1
  # PATCH/PUT /notifiers/1.json
  def update
    respond_to do |format|
      if @notifier.update(notifier_params)
        format.html { redirect_to @notifier, notice: 'Notifier was successfully updated.' }
        format.json { render :show, status: :ok, location: @notifier }
      else
        format.html { render :edit }
        format.json { render json: @notifier.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /notifiers/1
  # DELETE /notifiers/1.json
  def destroy
    @notifier.destroy
    respond_to do |format|
      format.html { redirect_to notifiers_url, notice: 'Notifier was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_notifier
      @notifier = Notifier.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def notifier_params
      params.require(:notifier).permit(:user_id, :kind, :settings, :notify_after_sec, :provider_id, :topic_id, :enabled).tap do |v|
        v[:settings] = YAML.load(v[:settings])
      end
    end
end


================================================
FILE: app/controllers/sessions_controller.rb
================================================
class SessionsController < ApplicationController
  skip_before_action :login_required, only: [:create]

  def create
    @user = User.find_or_create_from_auth_hash(auth_hash)
    @user.update_credentials_from_auth_hash(auth_hash)
    self.current_user = @user
    redirect_to '/'
  end

  private

  def auth_hash
    request.env['omniauth.auth']
  end
end


================================================
FILE: app/controllers/slack_controller.rb
================================================
class SlackController < ApplicationController
  skip_before_action :login_required, only: [:interactive], raise: false

  def interactive
    verify!

    message = payload['original_message']
    message['attachments'][0].delete('actions')

    user = payload['user']['name']

    text = ''
    payload['actions'].each do |a|
      case a['value']
      when 'acknowledge'
        incident.acknowledge!
        text = ":white_check_mark: @#{user} acknowledged"
      when 'resolve'
        incident.resolve!
        text = ":white_check_mark: @#{user} resolved"
      end
    end

    message['attachments'][0]['fields'] = [{
      'title' => text,
      'value' => '',
      'short' => false,
    }]

    render json: message
  end

  private def payload
    JSON.parse(params[:payload])
  end

  private def verify!
    verified = false
    Notifier.preload(:provider).find_each do |n|
      settings = n.provider.settings.merge(n.settings)
      token = settings['verification_token']
      if n.provider.slack? && settings['enable_buttons'] && payload['token'] == token
        verified = true
        break
      end
    end

    unless verified
      raise 'token verification failed'
    end
  end

  private def incident_id
    payload['callback_id'].match(/\Aincident\.(\d+)\z/)[1].to_i
  end

  private def incident
    Incident.find(incident_id)
  end
end


================================================
FILE: app/controllers/topics_controller.rb
================================================
class TopicsController < ApplicationController
  before_action :set_topic, only: [:show, :edit, :update, :destroy, :mailgun, :mackerel, :alertmanager, :slack]
  skip_before_action :login_required, only: [:mailgun, :mackerel, :alertmanager, :slack], raise: false

  # GET /topics
  # GET /topics.json
  def index
    @topics = Topic.all
  end

  # GET /topics/1
  # GET /topics/1.json
  def show
  end

  # GET /topics/new
  def new
    @topic = Topic.new
  end

  # GET /topics/1/edit
  def edit
  end

  # POST /topics
  # POST /topics.json
  def create
    @topic = Topic.new(topic_params)

    respond_to do |format|
      if @topic.save
        format.html { redirect_to @topic, notice: 'Topic was successfully created.' }
        format.json { render :show, status: :created, location: @topic }
      else
        format.html { render :new }
        format.json { render json: @topic.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /topics/1
  # PATCH/PUT /topics/1.json
  def update
    respond_to do |format|
      if @topic.update(topic_params)
        format.html { redirect_to @topic, notice: 'Topic was successfully updated.' }
        format.json { render :show, status: :ok, location: @topic }
      else
        format.html { render :edit }
        format.json { render json: @topic.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /topics/1
  # DELETE /topics/1.json
  def destroy
    @topic.destroy
    respond_to do |format|
      format.html { redirect_to topics_url, notice: 'Topic was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  # POST /topics/1/mailgun
  def mailgun
    unless @topic.enabled
      Rails.logger.info "Incident creation is skipped because the topic is disabled."
      render json: {}, status: 200
      return
    end

    # http://documentation.mailgun.com/user_manual.html#routes
    subject = params[:subject]
    description = params['body-plain']

    if @topic.in_maintenance?(subject, description)
      Rails.logger.info "Incident creation is skipped because the topic is in maintenance."
      render json: {}, status: 200
      return
    end

    @topic.incidents.create!(
      subject: subject,
      description: description,
    )

    render json: {}, status: 200
  end

  # POST /topics/1/mackerel
  def mackerel
    data = JSON.parse(request.body.read)
    return  render json: {}, status: 200 if data.dig('alert', 'status') == 'ok' || data.dig('alertGroup', 'status') == 'OK'

    unless @topic.enabled
      Rails.logger.info "Incident creation is skipped because the topic is disabled."
      render json: {}, status: 200
      return
    end

    if data['event'] == 'alertGroup' then
      subject = "[#{data['alertGroup']['status']}] #{data['alertGroupSetting']['name']}"
    else
      name = data.key?('host') ?  data['host']['name'] : data['alert']['monitorName']
      subject = "[#{data['alert']['status']}] #{name}"
    end
    description = JSON.pretty_generate(data)

    if @topic.in_maintenance?(subject, description)
      Rails.logger.info "Incident creation is skipped because the topic is in maintenance"
      render json: {}, status: 200
      return
    end

    @topic.incidents.create!(
      subject: subject,
      description: description,
    )
    render json: {}, status: 200
  end

  # POST /topics/1/alertmanager
  def alertmanager
    data = JSON.parse(request.body.read)

    unless @topic.enabled
      Rails.logger.info "Incident creation is skipped because the topic is disabled."
      render json: {}, status: 200
      return
    end

    subject = "[#{data['commonLabels']['severity']}] #{data['commonLabels']['alertname']}: #{data['commonAnnotations']['summary']}"
    description = JSON.pretty_generate(data)

    if @topic.in_maintenance?(subject, description)
      Rails.logger.info "Incident creation is skipped because the topic is in maintenance"
      render json: {}, status: 200
      return
    end

    @topic.incidents.create!(
      subject: subject,
      description: description,
    )
    render json: {}, status: 200
  end

  def slack
    unless @topic.enabled
      Rails.logger.info "Incident creation is skipped because the topic is disabled."
      render json: {}, status: 200
      return
    end

    subject = "escalation from slack,channel name:#{params['channel_name']} #{params['text']}"
    description = "channel:#{params['channel_name']} user:#{params['user_name']} #{params['text']}"

    if @topic.in_maintenance?(subject, description)
      Rails.logger.info "Incident creation is skipped because the topic is in maintenance."
      render json: {}, status: 200
      return
    end

    @topic.incidents.create!(
      subject: subject,
      description: description,
    )

    render json: {text: 'accept your page'}, status: 200
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_topic
      @topic = Topic.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def topic_params
      params.require(:topic).permit(:name, :kind, :escalation_series_id, :enabled)
    end
end


================================================
FILE: app/controllers/users_controller.rb
================================================
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy, :activation, :deactivation]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  # PATCH/PUT /users/1/activation
  def activation
    @user.update!(active: true)
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully activated.' }
    end
  end

  # PATCH/PUT /users/1/deactivation
  def deactivation
    @user.update!(active: false)
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully deactivated.' }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name)
    end
end


================================================
FILE: app/helpers/application_helper.rb
================================================
module ApplicationHelper
end


================================================
FILE: app/helpers/comments_helper.rb
================================================
module CommentsHelper
end


================================================
FILE: app/helpers/escalation_series_helper.rb
================================================
module EscalationSeriesHelper
end


================================================
FILE: app/helpers/escalations_helper.rb
================================================
module EscalationsHelper
end


================================================
FILE: app/helpers/home_helper.rb
================================================
module HomeHelper
end


================================================
FILE: app/helpers/incident_events_helper.rb
================================================
module IncidentEventsHelper
end


================================================
FILE: app/helpers/incidents_helper.rb
================================================
module IncidentsHelper
end


================================================
FILE: app/helpers/maintenances_helper.rb
================================================
module MaintenancesHelper
end


================================================
FILE: app/helpers/notifier_providers_helper.rb
================================================
module NotifierProvidersHelper
end


================================================
FILE: app/helpers/notifiers_helper.rb
================================================
module NotifiersHelper
end


================================================
FILE: app/helpers/sessions_helper.rb
================================================
module SessionsHelper
end


================================================
FILE: app/helpers/slack_helper.rb
================================================
module SlackHelper
end


================================================
FILE: app/helpers/topics_helper.rb
================================================
module TopicsHelper
end


================================================
FILE: app/helpers/users_helper.rb
================================================
module UsersHelper
end


================================================
FILE: app/mailers/.keep
================================================


================================================
FILE: app/models/.keep
================================================


================================================
FILE: app/models/application_record.rb
================================================
# Base ApplicationRecord Class
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end


================================================
FILE: app/models/comment.rb
================================================
class Comment < ApplicationRecord
  belongs_to :incident
  belongs_to :user

  validates :incident, presence: true
  validates :user, presence: true
  validates :comment, presence: true
end


================================================
FILE: app/models/concerns/.keep
================================================


================================================
FILE: app/models/escalation.rb
================================================
class Escalation < ApplicationRecord
  belongs_to :escalation_series
  belongs_to :escalate_to, class_name: 'User'

  validates :escalation_series, presence: true
  validates :escalate_to, presence: true
  validates :escalate_after_sec, numericality: {greater_than_or_equal_to: 5}
end


================================================
FILE: app/models/escalation_series.rb
================================================
class EscalationSeries < ApplicationRecord
  has_many :escalations, dependent: :destroy
  has_many :topics, dependent: :destroy

  validates :name, presence: true

  serialize :settings, JSON
  after_initialize :set_defaults

  def set_defaults
    self.settings ||= {}
  end

  def update_escalations!
    updater_class = case self.settings['update_by']
                    when 'google_calendar'
                      GoogleCalendarEscalationUpdater
                    else
                      nil
                    end
    if updater_class
      updater = updater_class.new(self)
      updater.update!
    end
  end

  class EscalationUpdater
    def initialize(series)
      @series = series
    end

    def update!
      raise NotImplementedError
    end

    private

    def settings
      @series.settings
    end
  end

  class GoogleCalendarEscalationUpdater < EscalationUpdater
    def initialize(*)
      super
      require 'google/api_client'
    end

    def update!
      Rails.logger.info "Update #{@series.inspect} by Google Calendar"

      if user_as.provider && user_as.provider != 'google_oauth2_with_calendar'
        raise "User ##{user_as.id} is not authenticated by 'google_oauth2_with_calendar' provider"
      end

      client = Google::APIClient.new(
        application_name: "Waker",
        application_version: "2.0.0",
        user_agent: "Waker/2.0.0 google-api-client"
      )
      auth = client.authorization

      expired = Time.at(user_as.credentials.fetch('expires_at')) < Time.now
      if user_as.credentials.fetch('expires') && expired
        Rails.logger.info "Refreshing access token..."

        auth.client_id = ENV["GOOGLE_CLIENT_ID"]
        auth.client_secret = ENV["GOOGLE_CLIENT_SECRET"]
        auth.refresh_token = user_as.credentials.fetch('refresh_token')
        auth.grant_type = "refresh_token"
        auth.refresh!

        user_as.update!(
          credentials: user_as.credentials.merge(
            'token' => auth.access_token,
            'expires_at' => auth.expires_at.to_i,
          )
        )
      else
        auth.access_token = user_as.credentials.fetch('token')
      end

      calendar_api = client.discovered_api('calendar', 'v3')

      calendar = client.execute(
        api_method: calendar_api.calendar_list.list,
        parameters: {},
      ).data.items.find do |cal|
        cal['summary'] == calendar_name
      end

      events = client.execute(
        api_method: calendar_api.events.list,
        parameters: {
          'calendarId' => calendar['id'],
          'timeMax' => (Time.now + 1).iso8601,
          'timeMin' => (Time.now).iso8601,
          'singleEvents' => true,
        },
      ).data.items

      events.each do |event|
        unless event['end']['dateTime'] && event['start']['dateTime']
          raise "dateTime field is not found (The event may be all-day event)\n#{event}"
        end
      end

      events.sort! do |a, b|
        a['end']['dateTime'] - a['start']['dateTime'] <=>
          b['end']['dateTime'] - b['start']['dateTime']
      end

      # shortest event
      event = events.first

      persons = event['summary'].split(event_delimiter).map(&:strip)
      escalations = @series.escalations.order('escalate_after_sec')

      persons.each_with_index do |name, i|
        user = User.find_by(name: name)
        raise "User '#{name}' is not found." unless user
        escalation = escalations[i]
        escalation.update!(escalate_to: user)
      end
    end

    private

    def user_as
      User.find(settings.fetch('user_as_id'))
    end

    def calendar_name
      settings.fetch('calendar')
    end

    def event_delimiter
      settings.fetch('event_delimiter')
    end
  end
end


================================================
FILE: app/models/escalation_update_worker.rb
================================================
class EscalationUpdateWorker
  include Sidekiq::Worker

  def perform
    EscalationSeries.all.each do |series|
      handle_series(series)
    end
  rescue => err
    Rails.logger.error "#{err.class}: #{err}\n#{err.backtrace.join("\n")}"
  end

  private

  def handle_series(series)
    series.update_escalations!
  end
end


================================================
FILE: app/models/escalation_worker.rb
================================================
class EscalationWorker
  include Sidekiq::Worker

  def self.enqueue(incident, escalation)
    Rails.logger.info "Enqueue EscalaionWorker job"
    self.perform_in(escalation.escalate_after_sec, incident.id, escalation.id)
  end

  def perform(incident_id, escalation_id)
    incident = Incident.find(incident_id)
    escalation = Escalation.find(escalation_id)

    if incident.opened?
      incident.events.create(
        kind: :escalated,
        info: {
          escalation: escalation,
          escalated_to: escalation.escalate_to,
        },
      )
    end
  end
end


================================================
FILE: app/models/incident.rb
================================================
require 'digest/sha1'

class Incident < ApplicationRecord
  STATUSES = [:opened, :acknowledged, :resolved]

  belongs_to :topic
  has_many :events, class_name: 'IncidentEvent', dependent: :destroy
  enum status: STATUSES
  has_many :comments

  validates :topic, presence: true
  validates :subject, presence: true
  validates :description, presence: true
  validates :occured_at, presence: true

  after_initialize :set_defaults
  after_create :enqueue

  def acknowledge!
    return if self.acknowledged? || self.resolved?

    self.acknowledged!

    events.create(kind: :acknowledged)
  end

  def resolve!
    return if self.resolved?

    self.resolved!

    events.create(kind: :resolved)
  end

  def confirmation_hash
    Digest::SHA1.hexdigest("#{Rails.application.secrets.secret_key_base}#{self.id}")
  end

  private

  def set_defaults
    self.status ||= :opened
    self.occured_at ||= Time.now
  end

  def enqueue
    topic.escalation_series.escalations.each do |escalation|
      EscalationWorker.enqueue(self, escalation)
    end

    events.create(kind: :opened)
  end
end


================================================
FILE: app/models/incident_event.rb
================================================
class IncidentEvent < ApplicationRecord
  belongs_to :incident

  enum kind: [:opened, :acknowledged, :resolved, :escalated, :commented, :notified]

  validates :incident, presence: true
  validates :kind, presence: true

  serialize :info, JSON
  after_create :notify
  after_initialize :set_defaults

  def set_defaults
    self.info ||= {}
  end

  def notify
    return if self.notified?

    Notifier.all.each do |notifier|
      next if notifier.topic && self.incident.topic != notifier.topic
      notifier.notify(self)
    end
  end

  def escalated_to
    self.info['escalated_to'] && User.find(self.info['escalated_to']['id'])
  end

  def escalation
    self.info['escalation'] && Escalation.find(self.info['escalation']['id'])
  end

  def notifier
    self.info['notifier'] && Notifier.find(self.info['notifier']['id'])
  end

  def event
    self.info['event'] && IncidentEvent.find(self.info['event']['id'])
  end
end


================================================
FILE: app/models/maintenance.rb
================================================
class Maintenance < ApplicationRecord
  scope :active, -> { t = Time.now; where('start_time <= ? AND ? <= end_time', t, t) }
  scope :not_expired, -> { where('? <= end_time', Time.now) }
  scope :expired,     -> { where('end_time < ?', Time.now) }

  belongs_to :topic

  def filter_regexp
    Regexp.new(filter)
  end
end


================================================
FILE: app/models/notification_worker.rb
================================================
class NotificationWorker
  include Sidekiq::Worker

  def self.enqueue(event:, notifier:)
    Rails.logger.info "Enqueue NotificationWorker job"

    self.perform_in(notifier.notify_after_sec, event.id, notifier.id)
  end

  def perform(event_id, notifier_id)
    event = IncidentEvent.find(event_id)
    notifier = Notifier.find(notifier_id)

    if notifier.enabled
      notifier.notify_immediately(event)
    end
  end
end


================================================
FILE: app/models/notifier.rb
================================================
class Notifier < ApplicationRecord
  belongs_to :provider, class_name: 'NotifierProvider'
  belongs_to :user
  belongs_to :topic

  validates :provider, presence: true
  validates :notify_after_sec, numericality: {greater_than_or_equal_to: 5}

  serialize :settings, JSON
  after_initialize :set_defaults

  def set_defaults
    self.settings ||= {}
  end

  def notify(event)
    NotificationWorker.enqueue(event: event, notifier: self)
  end

  def notify_immediately(event)
    provider.notify(event: event, notifier: self)
  end
end


================================================
FILE: app/models/notifier_provider.rb
================================================
class NotifierProvider < ApplicationRecord
  serialize :settings, JSON
  enum kind: [:mailgun, :file, :rails_logger, :hipchat, :twilio, :slack, :datadog, :sns]

  validates :name, presence: true

  after_initialize :set_defaults

  def set_defaults
    self.settings ||= {}
  end

  def concrete_class
    self.class.const_get("#{kind.to_s.camelize}ConcreteProvider")
  end

  def notify(event:, notifier:)
    concrete_class.new(provider: self, notifier: notifier, event: event).notify
  end

  class ConcreteProvider
    def initialize(provider:, notifier:, event:)
      @provider = provider
      @notifier = notifier
      @event = event
    end

    def notify
      if skip?
        Rails.logger.info "Notification skipped."
      else
        if target_events.include?(kind_of_event)
          _notify
          @event.incident.events.create(
            kind: :notified,
            info: {notifier: @notifier, event: @event}
          )
        else
          Rails.logger.info "Notification skipped due to target events (#{target_events} doesn't include #{kind_of_event})"
        end
      end
    end

    def _notify
      raise NotImplementedError
    end

    def settings
      @provider.settings.merge(@notifier.settings)
    end

    def skip?
      # or_conditions = [
      #   {
      #     'only_japanese_weekday' => true,
      #     'not_between' => '9:30-18:30',
      #   },
      #   {
      #     'not_japanese_weekday' => true,
      #   }
      # ]
      return skip_due_to_or_conditions? ||
        skip_due_to_status_of_incident?
    end

    def skip_due_to_status_of_incident?
      if !@event.incident.opened? && !([:acknowledged, :resolved].include?(kind_of_event))
        return true
      end

      false
    end

    def skip_due_to_or_conditions?
      or_conditions = settings['or_conditions']
      return false unless or_conditions

      matched = or_conditions.any? do |condition|
        # japanese_weekday
        holiday = HolidayJp.holiday?(Date.today) || Time.now.saturday? || Time.now.sunday?

        if holiday && condition['japanese_weekday']
          next false
        end

        if !holiday && condition['not_japanese_weekday']
          next false
        end

        # between
        skip_due_to_between_condition = %w!between not_between!.any? do |k|
          if s = condition[k]
            start_time, end_time = s.split('-')
            start_time = Time.parse(start_time)
            end_time   = Time.parse(end_time)
            between = start_time < Time.now && Time.now < end_time
            if (between && k == 'not_between') || (!between && k == 'between')
              next true
            end
          end

          false
        end

        next false if skip_due_to_between_condition

        true
      end

      return !matched
    end

    def all_events
      [:escalated, :escalated_to_me, :opened, :acknowledged, :resolved, :commented]
    end

    def target_events
      settings['events'] &&
        settings['events'].map {|v| v.to_sym }
    end

    def body(formats: [:text])
      template_names = [kind_of_event, 'default']
      template_names.each_with_index do |template_name, i|
        begin
          rendered = ApplicationController.new.render_to_string(
            template: "notifier_providers/#{@provider.kind}/#{template_name}",
            formats: formats,
            layout: nil,
            locals: {event: @event},
          )

          return rendered.strip
        rescue ActionView::MissingTemplate
          raise if template_names.size - 1 == i
        end
      end
    end

    def kind_of_event
      if @event.escalated? && @event.escalated_to == @notifier.user
        :escalated_to_me
      else
        @event.kind.to_sym
      end
    end
  end

  class FileConcreteProvider < ConcreteProvider
    def _notify
    end
  end

  class RailsLoggerConcreteProvider < ConcreteProvider
    def _notify
      Rails.logger.info(body)
    end

    def target_events
      all_events
    end
  end

  class HipchatConcreteProvider < ConcreteProvider
    def _notify
      case kind_of_event
      when :opened
        color = 'red'
      when :acknowledged, :escalated
        color = 'yellow'
      when :resolved
        color = 'green'
      else
        return
      end

      client = HipChat::Client.new(api_token, api_version: api_version)
      client[room].send('Waker', body, color: color, notify: notify?)
    end

    private

    def api_token
      settings.fetch('api_token')
    end

    def room
      settings.fetch('room')
    end

    def api_version
      case settings.fetch('api_version')
      when '2', 'v2'
        'v2'
      when '1', 'v1'
        'v1'
      else
        'v2'
      end
    end

    def notify?
      !!settings['notify']
    end

    def target_events
      super || [:escalated, :opened, :acknowledged, :resolved]
    end
  end

  class MailgunConcreteProvider < ConcreteProvider
    def _notify
      conn = Faraday.new(url: 'https://api.mailgun.net') do |faraday|
        faraday.request  :url_encoded
        faraday.response :logger
        faraday.adapter  Faraday.default_adapter
      end

      conn.basic_auth('api', api_key)

      response = conn.post "/v2/#{domain}/messages", {
        from: from,
        to: to,
        subject: "[Waker] #{@event.incident.subject}",
        text: body,
        html: body(formats: [:html]),
      }

      Rails.logger.info "response status: #{response.status}"
      Rails.logger.info JSON.parse(response.body)
    end

    private
    def api_key
      settings.fetch('api_key')
    end

    def domain
      from.split('@').last
    end

    def from
      settings.fetch('from')
    end

    def to
      settings.fetch('to')
    end

    def target_events
      super || [:escalated_to_me]
    end
  end

  class TwilioConcreteProvider < ConcreteProvider
    def _notify
      options = {}
      options[:user] =     basic_auth_user if basic_auth_user
      options[:password] = basic_auth_password if basic_auth_password

      url = Rails.application.routes.url_helpers.twilio_incident_event_url(
        @event, options
      )

      Twilio::REST::Client.new(account_sid, auth_token).calls.create(
        from: from,
        to: to,
        url: url,
      )
    end

    def account_sid
      settings.fetch('account_sid')
    end

    def auth_token
      settings.fetch('auth_token')
    end

    def from
      settings.fetch('from')
    end

    def to
      settings.fetch('to')
    end

    def target_events
      super || [:escalated_to_me]
    end

    def basic_auth_user
      ENV['BASIC_AUTH_USER']
    end

    def basic_auth_password
      ENV['BASIC_AUTH_PASSWORD']
    end
  end

  class SlackConcreteProvider < ConcreteProvider
    def _notify
      fields = []

      acknowledge_url = Rails.application.routes.url_helpers.acknowledge_incident_url(@event.incident, hash: @event.incident.confirmation_hash)
      resolve_url = Rails.application.routes.url_helpers.resolve_incident_url(@event.incident, hash: @event.incident.confirmation_hash)
      comment_url = Rails.application.routes.url_helpers.new_incident_comment_url(@event.incident)
      actions = []
      case kind_of_event
      when :opened
        color = 'danger'
        title = 'New incident opened'
        if buttons_enabled?
          action_links = "<#{comment_url}|Comment>"
          actions = [:acknowledge, :resolve]
        else
          action_links = "<#{acknowledge_url}|Acknowledge> or <#{resolve_url}|Resolve> | <#{comment_url}|Comment>"
        end
      when :acknowledged
        color = 'warning'
        title = 'Incident acknowledged'
        if buttons_enabled?
          action_links = "<#{comment_url}|Comment>"
          actions = [:resolve]
        else
          action_links = "<#{resolve_url}|Resolve> | <#{comment_url}|Comment>"
        end
      when :escalated
        color = 'warning'
        title = "Incident escalated to #{@event.escalated_to.name}"
        if buttons_enabled?
          action_links = "<#{comment_url}|Comment>"
          actions = [:acknowledge, :resolve]
        else
          action_links = "<#{acknowledge_url}|Acknowledge> or <#{resolve_url}|Resolve> | <#{comment_url}|Comment>"
        end
      when :resolved
        color = 'good'
        title = 'Incident resolved'
        action_links = "<#{comment_url}|Comment>"
      end

      text = @event.incident.subject
      if action_links
        text += " (#{action_links})"
      end

      action_types = {
        acknowledge: {
          "name" => "response",
          "text" => "Acknowledge",
          "type" => "button",
          "value" => "acknowledge",
        },
        resolve: {
          "name" => "response",
          "text" => "Resolve",
          "type" => "button",
          "value" => "resolve",
          "style" => "primary",
        },
      }

      attachments = [{
        "fallback" => "[#{kind_of_event.to_s.capitalize}] #{@event.incident.subject}",
        "color" => color,
        "title" => title,
        "text" => text,
        "fields" => fields,
        "callback_id" => "incident.#{@event.incident.id}",
        "actions" => actions.map {|t| action_types[t] },
      }]

      payload = {'attachments' => attachments}
      if channel
        payload['channel'] = channel
      end

      url = URI.parse(webhook_url)
      conn = Faraday.new(url: "#{url.scheme}://#{url.host}") do |faraday|
        faraday.adapter Faraday.default_adapter
      end

      conn.post do |req|
        req.url url.path
        req.headers['Content-Type'] = 'application/json'
        req.body = payload.to_json
      end
    end

    private

    def webhook_url
      settings.fetch('webhook_url')
    end

    def channel
      settings['channel']
    end

    def buttons_enabled?
      settings['enable_buttons']
    end

    def target_events
      [:escalated, :opened, :acknowledged, :resolved]
    end
  end

  class DatadogConcreteProvider < ConcreteProvider
    def _notify
      dog = Dogapi::Client.new(api_key, app_key)

      res = dog.emit_event(Dogapi::Event.new(
              @event.incident.description, {
                msg_title: @event.incident.subject,
                alert_type: alert_type,
                tags: tags,
                source_type_name: source_type_name,
              }
            ))

      Rails.logger.info res
    end

    private

    def api_key
      settings.fetch('api_key')
    end

    def app_key
      settings.fetch('app_key')
    end

    def alert_type
      settings['alert_type'] || 'error'
    end

    def tags
      settings['tags'] || 'waker'
    end

    def source_type_name
      settings['source_type_name'] || 'waker'
    end

    def target_events
      [:opened]
    end
  end

  class SnsConcreteProvider < ConcreteProvider
    def _notify
      aws_config = {}
      aws_config[:region] = region if region
      aws_config[:access_key_id] = access_key_id if access_key_id
      aws_config[:secret_access_key] = secret_access_key if secret_access_key

      sns = Aws::SNS::Client.new(aws_config)

      res = sns.publish(
        topic_arn: topic_arn,
        message: @event.incident.description,
        subject: @event.incident.subject,
        message_attributes: {
          'kind_of_event' => {
            data_type: 'String',
            string_value: kind_of_event.to_s,
          }
        }
      )

      Rails.logger.info res
    end

    private

    def topic_arn
      settings.fetch('topic_arn')
    end

    def region
      settings['region']
    end

    def access_key_id
      settings['access_key_id']
    end

    def secret_access_key
      settings['secret_access_key']
    end

    def target_events
      all_events
    end
  end
end


================================================
FILE: app/models/topic.rb
================================================
class Topic < ApplicationRecord
  enum kind: [:api]
  belongs_to :escalation_series
  has_many :incidents

  validates :name, presence: true
  validates :kind, presence: true
  validates :escalation_series, presence: true

  def in_maintenance?(*bodies)
    maints = Maintenance.active.where(topic: self)
    maints.any? do |m|
      if m.filter.blank?
        true
      else
        r = m.filter_regexp
        bodies.any? do |body|
          !!r.match(body)
        end
      end
    end
  end
end


================================================
FILE: app/models/user.rb
================================================
require 'securerandom'

class User < ApplicationRecord
  scope :active, -> { where(active: true) }

  has_many :notifiers

  serialize :credentials, JSON

  validates :name, presence: true

  before_save :set_defaults

  def self.find_or_create_from_auth_hash(auth_hash)
    user = self.find_by(provider: auth_hash[:provider], uid: auth_hash[:uid])
    return user if user

    user = self.find_by(email: auth_hash[:info][:email])
    if user
      # email is deprecated
      user.update!(provider: auth_hash[:provider], uid: auth_hash[:uid], email: nil)
      return user
    end

    self.create!(provider: auth_hash[:provider], uid: auth_hash[:uid], name: auth_hash[:info][:name])
  end

  def update_credentials_from_auth_hash(auth_hash)
    self.update!(credentials: auth_hash.fetch(:credentials))
  end

  private

  def set_defaults
    self.login_token ||= SecureRandom.hex
  end
end


================================================
FILE: app/views/comments/_form.html.erb
================================================
<%= form_for([@incident, @comment]) do |f| %>
  <% if @comment.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@comment.errors.count, "error") %> prohibited this comment from being saved:</h2>

      <ul>
      <% @comment.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <%= f.hidden_field :incident_id, value: @incident.id %>
  <%= f.hidden_field :user_id, value: current_user.id %>

  <p>
    <strong>Incident:</strong>
    <pre><%= @incident.subject %></pre>
  </p>

  <div class="form-group">
    <%= f.label :comment %>
    <%= f.text_area :comment, class: 'form-control', rows: 8 %>
  </div>

  <div class="actions">
    <%= f.submit(class: 'btn btn-default') %>
  </div>
<% end %>


================================================
FILE: app/views/comments/edit.html.erb
================================================
<h1>Editing Comment</h1>

<%= render 'form' %>

<%= link_to 'Show', [@incident, @comment] %> |
<%= link_to 'Back', incident_comments_path %>


================================================
FILE: app/views/comments/index.html.erb
================================================
<h1>Listing Comments</h1>

<p>
  <strong>Incident:</strong>
  <pre><%= @incident.subject %></pre>
</p>

<table class="table">
  <thead>
    <tr>
      <th>Comment</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @comments.each do |comment| %>
      <tr>
        <td><%= comment.comment %> (<%= comment.user.name %>)</td>
        <td><%= link_to 'Show', [@incident, comment] %></td>
        <td><%= link_to 'Edit', edit_incident_comment_path(@incident, comment) %></td>
        <td><%= link_to 'Destroy', [@incident, comment], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Comment', new_incident_comment_path %> |
<%= link_to 'Incident', @incident %>



================================================
FILE: app/views/comments/index.json.jbuilder
================================================
json.array!(@comments) do |comment|
  json.extract! comment, :id
  json.url comment_url(comment, format: :json)
end


================================================
FILE: app/views/comments/new.html.erb
================================================
<h1>New Comment</h1>

<%= render 'form' %>

<%= link_to 'Back', incident_comments_path %>


================================================
FILE: app/views/comments/show.html.erb
================================================

<p>
  <strong>Incident:</strong>
  <pre><%= @incident.subject %></pre>
</p>

<p>
  <strong>Comment:</strong>
  <pre style=><%= auto_link(@comment.comment, :html => { :target => '_blank' }) %></pre>
</p>

<p>
  <strong>User:</strong>
  <pre><%= @comment.user.name %></pre>
</p>

<%= link_to 'Edit', edit_incident_comment_path(@incident, @comment) %> |
<%= link_to 'Back', incident_comments_path %>


================================================
FILE: app/views/comments/show.json.jbuilder
================================================
json.extract! @comment, @user, :id, :created_at, :updated_at


================================================
FILE: app/views/escalation_series/_form.html.erb
================================================
<%= form_for(@escalation_series) do |f| %>
  <% if @escalation_series.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@escalation_series.errors.count, "error") %> prohibited this escalation_series from being saved:</h2>

      <ul>
      <% @escalation_series.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :name %>
    <%= f.text_field :name, required: true, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :settings %>
    <%= f.text_area :settings, value: @escalation_series.settings.to_yaml, class: 'form-control', rows: 8 %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/escalation_series/edit.html.erb
================================================
<h1>Editing Escalation Series</h1>

<%= render 'form' %>

<%= link_to 'Show', @escalation_series %> |
<%= link_to 'Back', escalation_series_index_path %>


================================================
FILE: app/views/escalation_series/index.html.erb
================================================

<h1>Listing Escalation Series</h1>

<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @escalation_series.each do |escalation_series| %>
      <tr>
        <td><%= escalation_series.name %></td>
        <td><%= link_to 'Show', escalation_series %></td>
        <td><%= link_to 'Edit', edit_escalation_series_path(escalation_series) %></td>
        <td><%= link_to 'Destroy', escalation_series, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Escalation series', new_escalation_series_path %>


================================================
FILE: app/views/escalation_series/index.json.jbuilder
================================================
json.array!(@escalation_series) do |escalation_series|
  json.extract! escalation_series, :id, :name
  json.url escalation_series_url(escalation_series, format: :json)
end


================================================
FILE: app/views/escalation_series/new.html.erb
================================================
<h1>New Escalation Series</h1>

<%= render 'form' %>

<%= link_to 'Back', escalation_series_index_path %>


================================================
FILE: app/views/escalation_series/show.html.erb
================================================

<p>
  <strong>Name:</strong>
  <%= @escalation_series.name %>
</p>

<table class="table">
  <% @escalation_series.escalations.order("escalate_after_sec").each do |escalation| %>
    <tr>
      <td><%= escalation.escalate_to.name %></td>
      <td>escalate after <%= escalation.escalate_after_sec %> sec</td>
    </tr>
  <% end %>
</table>
<ul>
</ul>

<%= link_to 'Edit', edit_escalation_series_path(@escalation_series) %> |
<%= link_to 'Back', escalation_series_index_path %>


================================================
FILE: app/views/escalation_series/show.json.jbuilder
================================================
json.extract! @escalation_series, :id, :name, :created_at, :updated_at


================================================
FILE: app/views/escalations/_form.html.erb
================================================
<%= form_for(@escalation) do |f| %>
  <% if @escalation.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@escalation.errors.count, "error") %> prohibited this escalation from being saved:</h2>

      <ul>
      <% @escalation.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :escalate_to_id %>
    <%= f.collection_select(:escalate_to_id, User.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :escalate_after_sec %>
    <%= f.number_field :escalate_after_sec, min: 5, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :escalation_series_id %>
    <%= f.collection_select(:escalation_series_id, EscalationSeries.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/escalations/edit.html.erb
================================================
<h1>Editing Escalation</h1>

<%= render 'form' %>

<%= link_to 'Show', @escalation %> |
<%= link_to 'Back', escalations_path %>


================================================
FILE: app/views/escalations/index.html.erb
================================================

<h1>Listing Escalations</h1>

<table class="table">
  <thead>
    <tr>
      <th>Escalation Series</th>
      <th>Escalate to</th>
      <th>Escalate after sec</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @escalations.each do |escalation| %>
      <tr>
        <td><%= escalation.escalation_series.name %></td>
        <td><%= escalation.escalate_to.name %></td>
        <td><%= escalation.escalate_after_sec %></td>
        <td><%= link_to 'Show', escalation %></td>
        <td><%= link_to 'Edit', edit_escalation_path(escalation) %></td>
        <td><%= link_to 'Destroy', escalation, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Escalation', new_escalation_path %>


================================================
FILE: app/views/escalations/index.json.jbuilder
================================================
json.array!(@escalations) do |escalation|
  json.extract! escalation, :id, :escalate_to_id, :escalate_after_sec, :escalation_series_id
  json.url escalation_url(escalation, format: :json)
end


================================================
FILE: app/views/escalations/new.html.erb
================================================
<h1>New Escalation</h1>

<%= render 'form' %>

<%= link_to 'Back', escalations_path %>


================================================
FILE: app/views/escalations/show.html.erb
================================================

<p>
  <strong>Escalation series:</strong>
  <%= @escalation.escalation_series.name %>
</p>

<p>
  <strong>Escalate to:</strong>
  <%= @escalation.escalate_to.name %>
</p>

<p>
  <strong>Escalate after sec:</strong>
  <%= @escalation.escalate_after_sec %>
</p>

<%= link_to 'Edit', edit_escalation_path(@escalation) %> |
<%= link_to 'Back', escalations_path %>


================================================
FILE: app/views/escalations/show.json.jbuilder
================================================
json.extract! @escalation, :id, :escalate_to_id, :escalate_after_sec, :created_at, :updated_at


================================================
FILE: app/views/home/index.html.erb
================================================
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>


================================================
FILE: app/views/incident_events/twilio.html.erb
================================================
<h1>IncidentEvents#twilio</h1>
<p>Find me in app/views/incident_events/twilio.html.erb</p>


================================================
FILE: app/views/incidents/_form.html.erb
================================================
<%= form_for(@incident) do |f| %>
  <% if @incident.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@incident.errors.count, "error") %> prohibited this incident from being saved:</h2>

      <ul>
      <% @incident.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :subject %>
    <%= f.text_field :subject, required: true, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :description %>
    <%= f.text_area :description, required: true, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :topic_id %>
    <%= f.collection_select(:topic_id, Topic.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :occured_at %>
    <%= f.datetime_select :occured_at %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/incidents/edit.html.erb
================================================
<h1>Editing Incident</h1>

<%= render 'form' %>

<%= link_to 'Show', @incident %> |
<%= link_to 'Back', incidents_path %>


================================================
FILE: app/views/incidents/index.html.erb
================================================

<h1>Listing Incidents</h1>

<div class="btn-toolbar" role="toolbar" style="margin-bottom: 15px">
  <div class="btn-group" role="group">
    <%-
    {
      nil => 'All',
      Incident.statuses.values_at(*%w!opened acknowledged!) => 'Opened or Acked',
      Incident.statuses.values_at(*%w!opened!) => 'Opened',
      Incident.statuses.values_at(*%w!acknowledged!) => 'Acked',
      Incident.statuses.values_at(*%w!resolved!) => 'Resolved',
    }.each do |statuses, desc| -%>
      <a href="<%= "?statuses=#{statuses && statuses.join(',')}" %>" class="btn btn-default">
        <%- if statuses == @visible_statuses %>
          <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
        <%- end -%>
        <%= desc %>
      </a>
    <%- end -%>
  </div>

  <div class="btn-group" role="group">
    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
      Topic
      <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" role="menu">
      <li><a href="?topic=all">
        <%- if @visible_topic.nil? -%>
          <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
        <%- end -%>
        All
      </a></li>
      <% Topic.all.each do |topic| %>
        <li><a href="?topic=<%= topic.id %>">
          <%- if @visible_topic == topic.id -%>
            <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
          <%- end -%>
          <%= topic.name %>
        </a></li>
      <% end %>
    </ul>
  </div>
</div>

<table class="table">
  <thead>
    <tr>
      <th>Status</th>
      <th>Subject</th>
      <th>Topic</th>
      <th>Occured at</th>
      <th colspan="4"></th>
    </tr>
  </thead>

  <tbody>
    <% @incidents.each do |incident| %>
      <tr>
        <td rowspan="<%= incident.comments.present? ? 2 : 1 %>">
          <% if incident.opened? %>
            <span class="label label-danger">Opened</span>
          <% elsif incident.acknowledged? %>
            <span class="label label-warning">Acknowledged</span>
          <% elsif incident.resolved? %>
            <span class="label label-success">Resolved</span>
          <% end %>
        </td>
        <td><%= incident.subject %></td>
        <td><%= incident.topic.name %></td>
        <td><%= incident.occured_at %></td>
        <td><%= link_to 'Show', incident %></td>
        <td><%= link_to 'Ack', acknowledge_incident_path(incident, hash: incident.confirmation_hash) %></td>
        <td><%= link_to 'Resolve', resolve_incident_path(incident, hash: incident.confirmation_hash) %></td>
        <td><%= link_to 'Comment', new_incident_comment_path(incident) %></td>
      </tr>
      <% if incident.comments.present? %>
        <tr>
          <td colspan="7">
            <% incident.comments.each_with_index do |comment, i| %>
              <pre style="font-size: xx-small;"><%= auto_link(comment.comment, :html => { :target => '_blank' }) %><br>(<%= comment.user.name %>)</pre>
            <% end %>
          </td>
        </tr>
      <% end %>
    <% end %>
  </tbody>
</table>

<%= paginate @incidents %>

<br>

<%= link_to 'Ack All', acknowledge_incidents_path, class: "btn btn-default", method: :patch, data: { confirm: "#{@incidents.count} incidents will be marked as acked. Are you sure?" } %>
<%= link_to 'Resolve All', resolve_incidents_path, class: "btn btn-success", method: :patch, data: { confirm: "#{@incidents.count} incidents will be marked as resolved. Are you sure?" } %>


================================================
FILE: app/views/incidents/index.json.jbuilder
================================================
json.array!(@incidents) do |incident|
  json.extract! incident, :id, :subject, :description, :topic_id, :occured_at
  json.url incident_url(incident, format: :json)
end


================================================
FILE: app/views/incidents/new.html.erb
================================================
<h1>New Incident</h1>

<%= render 'form' %>

<%= link_to 'Back', incidents_path %>


================================================
FILE: app/views/incidents/show.html.erb
================================================

<p>
  <strong>Subject:</strong>
  <%= @incident.subject %>
</p>

<p>
  <strong>Description:</strong>
  <pre><%= @incident.description %></pre>
</p>

<p>
  <strong>Topic:</strong>
  <%= @incident.topic.name %>
</p>

<p>
  <strong>Occured at:</strong>
  <%= @incident.occured_at %>
</p>

<table class="table">
  <tr>
    <th>Time</th>
    <th>Event</th>
    <th>Description</th>
  </tr>
  <% @incident.events.each do |event| %>
    <tr>
      <td><%= event.created_at %></td>
      <td><%= event.kind %></td>
      <td>
        <% if event.escalated? %>
          to <%= link_to(event.escalated_to.name, event.escalated_to) %>
        <% elsif event.notified? %>
          notified by <%= link_to('notifier', event.notifier) %>
        <% end %>
      </td>
    </tr>
  <% end %>
</table>

<p>
  <strong>Comment:</strong>
</p>

<table class="table">
  <tr>
    <th>Time</th>
    <th>Comment</th>
  </tr>
  <% @incident.comments.each do |comment| %>
    <tr>
      <td><%= comment.created_at %></td>
      <td><%= comment.comment %> (<%= comment.user.name %>)</td>
    </tr>
  <% end %>
</table>

<%= link_to 'Edit', edit_incident_path(@incident) %> |
<%= link_to 'Destroy', @incident, method: :delete, data: { confirm: 'Are you sure?' } %> |
<%= link_to 'Back', incidents_path %>


================================================
FILE: app/views/incidents/show.json.jbuilder
================================================
json.extract! @incident, :id, :subject, :description, :topic_id, :occured_at, :created_at, :updated_at


================================================
FILE: app/views/layouts/application.html.erb
================================================
<!DOCTYPE html>
<html>
<head>
  <title>Waker</title>
  <%= stylesheet_link_tag    'application', media: 'all' %>
  <%= javascript_include_tag 'application' %>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
  <style>
body {
  padding-top: 70px;
}
  </style>

  <%= csrf_meta_tags %>
</head>
<body>

<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/">Waker</a>
    </div>
    <div id="navbar" class="collapse navbar-collapse">
      <ul class="nav navbar-nav">
        <li><%= link_to 'Incidents', incidents_path %></li>
        <li><%= link_to 'Topics', topics_path %></li>
        <li><%= link_to 'Escalation Series', escalation_series_index_path %></li>
        <li><%= link_to 'Escalations', escalations_path %></li>
        <li><%= link_to 'Users', users_path %></li>
        <li><%= link_to 'Notifiers', notifiers_path %></li>
        <li><%= link_to 'Notifier Providers', notifier_providers_path %></li>
        <li><%= link_to 'Maintenances', maintenances_path %></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
        <%- if current_user -%>
          <p class="navbar-text">Logged in as <%= current_user.name %></p>
        <%- end -%>
      </ul>
    </div><!--/.nav-collapse -->
  </div>
</nav>

<div class="container">
  <% if notice %>
    <div class="alert alert-success" role="alert"><%= notice %></div>
  <% end %>
  <%= yield %>
</div><!-- /.container -->

<!-- Latest compiled and minified JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
</body>
</html>


================================================
FILE: app/views/maintenances/_form.html.erb
================================================
<%= form_for(@maintenance) do |f| %>
  <% if @maintenance.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@maintenance.errors.count, "error") %> prohibited this maintenance from being saved:</h2>

      <ul>
      <% @maintenance.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :topic_id %><br>
    <%= f.collection_select(:topic_id, Topic.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :filter %><br>
    <%= f.text_field(:filter, class: 'form-control', placeholder: 'regexp') %>
  </div>
  <div class="form-group">
    <%= f.label :start_time %><br>
    <%= f.datetime_select :start_time, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :end_time %><br>
    <%= f.datetime_select :end_time, class: 'form-control' %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/maintenances/edit.html.erb
================================================
<h1>Editing Maintenance</h1>

<%= render 'form' %>

<%= link_to 'Show', @maintenance %> |
<%= link_to 'Back', maintenances_path %>


================================================
FILE: app/views/maintenances/index.html.erb
================================================

<h1>Listing Maintenances</h1>

<p>Expired maintanances are not shown</p>

<table class="table">
  <thead>
    <tr>
      <th>Topic</th>
      <th>Filter Regexp</th>
      <th>Start time</th>
      <th>End time</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @maintenances.each do |maintenance| %>
      <tr>
        <td><%= maintenance.topic.name %></td>
        <td><code><%= maintenance.filter %></code></td>
        <td><%= maintenance.start_time %></td>
        <td><%= maintenance.end_time %></td>
        <td><%= link_to 'Show', maintenance %></td>
        <td><%= link_to 'Edit', edit_maintenance_path(maintenance) %></td>
        <td><%= link_to 'Destroy', maintenance, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Maintenance', new_maintenance_path %>


================================================
FILE: app/views/maintenances/index.json.jbuilder
================================================
json.array!(@maintenances) do |maintenance|
  json.extract! maintenance, :id, :topic_id, :start_time, :end_time
  json.url maintenance_url(maintenance, format: :json)
end


================================================
FILE: app/views/maintenances/new.html.erb
================================================
<h1>New Maintenance</h1>

<%= render 'form' %>

<%= link_to 'Back', maintenances_path %>


================================================
FILE: app/views/maintenances/show.html.erb
================================================
<p id="notice"><%= notice %></p>

<p>
  <strong>Topic:</strong>
  <%= @maintenance.topic.name %>
</p>

<p>
  <strong>Filter (regexp):</strong>
  <code><%= @maintenance.filter %></code>
</p>

<p>
  <strong>Start time:</strong>
  <%= @maintenance.start_time %>
</p>

<p>
  <strong>End time:</strong>
  <%= @maintenance.end_time %>
</p>

<%= link_to 'Edit', edit_maintenance_path(@maintenance) %> |
<%= link_to 'Back', maintenances_path %>


================================================
FILE: app/views/maintenances/show.json.jbuilder
================================================
json.extract! @maintenance, :id, :topic_id, :start_time, :end_time, :created_at, :updated_at


================================================
FILE: app/views/notifier_providers/_form.html.erb
================================================
<%= form_for(@notifier_provider) do |f| %>
  <% if @notifier_provider.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@notifier_provider.errors.count, "error") %> prohibited this notifier_provider from being saved:</h2>

      <ul>
      <% @notifier_provider.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :name %>
    <%= f.text_field :name, required: true, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :kind %>
    <%= f.select :kind, nil, {}, class: 'form-control' do %>
      <%= options_for_select(
        NotifierProvider.kinds.map do |kind, _|
          [kind.camelize, kind, {}]
        end, @notifier_provider.kind
      ) %>
    <% end %>
  </div>
  <div class="form-group">
    <%= f.label :settings %>
    <%= f.text_area :settings, value: @notifier_provider.settings.to_yaml, class: 'form-control', rows: 8 %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/notifier_providers/edit.html.erb
================================================
<h1>Editing Notifier Provider</h1>

<%= render 'form' %>

<%= link_to 'Show', @notifier_provider %> |
<%= link_to 'Back', notifier_providers_path %>


================================================
FILE: app/views/notifier_providers/hipchat/acknowledged.text.erb
================================================
Incident acknowledged: <%= event.incident.subject %> (<a href="<%= Rails.application.routes.url_helpers.resolve_incident_url(event.incident, hash: event.incident.confirmation_hash) %>">Resolve</a>)


================================================
FILE: app/views/notifier_providers/hipchat/escalated.text.erb
================================================
Incident escalated to <%= event.escalated_to.name %>: <%= event.incident.subject %> (<a href="<%= Rails.application.routes.url_helpers.acknowledge_incident_url(event.incident, hash: event.incident.confirmation_hash) %>">Acknowledge</a>) (<a href="<%= Rails.application.routes.url_helpers.resolve_incident_url(event.incident, hash: event.incident.confirmation_hash) %>">Resolve</a>)


================================================
FILE: app/views/notifier_providers/hipchat/opened.text.erb
================================================
New incident opened: <%= event.incident.subject %> (<a href="<%= Rails.application.routes.url_helpers.acknowledge_incident_url(event.incident, hash: event.incident.confirmation_hash) %>">Acknowledge</a>) (<a href="<%= Rails.application.routes.url_helpers.resolve_incident_url(event.incident, hash: event.incident.confirmation_hash) %>">Resolve</a>)


================================================
FILE: app/views/notifier_providers/hipchat/resolved.text.erb
================================================
Incident resolved: <%= event.incident.subject %>


================================================
FILE: app/views/notifier_providers/index.html.erb
================================================

<h1>Listing Notifier Providers</h1>

<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Kind</th>
      <th>Settings</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @notifier_providers.each do |notifier_provider| %>
      <tr>
        <td><%= notifier_provider.name %></td>
        <td><%= notifier_provider.kind %></td>
        <td><%= notifier_provider.settings %></td>
        <td><%= link_to 'Show', notifier_provider %></td>
        <td><%= link_to 'Edit', edit_notifier_provider_path(notifier_provider) %></td>
        <td><%= link_to 'Destroy', notifier_provider, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Notifier provider', new_notifier_provider_path %>


================================================
FILE: app/views/notifier_providers/index.json.jbuilder
================================================
json.array!(@notifier_providers) do |notifier_provider|
  json.extract! notifier_provider, :id, :name, :kind, :settings
  json.url notifier_provider_url(notifier_provider, format: :json)
end


================================================
FILE: app/views/notifier_providers/mailgun/default.html.erb
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>[Waker] <%= event.incident.subject %></title>
  </head>
  <body>
    <%
      ack_url = Rails.application.routes.url_helpers.acknowledge_incident_url(event.incident, hash: event.incident.confirmation_hash)
      ack_url_json = Rails.application.routes.url_helpers.acknowledge_incident_url(event.incident, hash: event.incident.confirmation_hash, format: :json)
      resolve_url = Rails.application.routes.url_helpers.resolve_incident_url(event.incident, hash: event.incident.confirmation_hash)

      email_markup = {
        "@context" => "http://schema.org",
        "@type" => "EmailMessage",
        "potentialAction" => {
          "@type" => "ConfirmAction",
          "name" => "Acknowledge incident",
          "handler" => {
            "@type" => "HttpActionHandler",
            "url" => ack_url_json,
          },
        },
        "description" => "Acknowledge incident: #{event.incident.subject}",
      }

    %>

    <script type="application/ld+json"><%= email_markup.to_json.html_safe %></script>

    <h1><%= event.incident.subject %></h1>

    <div>
      <%
        btn_style = 'border-radius: 4px;' \
          'text-decoration: none;' \
          'display: inline-block;' \
          'font-size: 1.3em;' \
          'padding: 5px 8px;' \
          'margin-right: 12px;' \
      %>
      <a href="<%= ack_url %>"><div style="<%= btn_style %>; border: 1px solid #ccc; color: black;">Acknowledge</div></a> <a href="<%= resolve_url %>"><div style="<%= btn_style %>; background-color: #5cb85c; color: white;">Resolve</div></a>
    </div>

    <div style="margin-top: 12px;">
      <pre style="font-size: 1.1em; white-space: pre; word-wrap: break-word;"><code>
<%= event.incident.description %>
      </code></pre>
    </div>
  </body>
</body>


================================================
FILE: app/views/notifier_providers/mailgun/default.text.erb
================================================
<%= event.incident.subject %>
====

Actions:

- To ack: <%= Rails.application.routes.url_helpers.acknowledge_incident_url(event.incident, hash: event.incident.confirmation_hash) %>
- To resolve: <%= Rails.application.routes.url_helpers.resolve_incident_url(event.incident, hash: event.incident.confirmation_hash) %>

----

<%= event.incident.description %>


================================================
FILE: app/views/notifier_providers/new.html.erb
================================================
<h1>New Notifier Provider</h1>

<%= render 'form' %>

<%= link_to 'Back', notifier_providers_path %>


================================================
FILE: app/views/notifier_providers/rails_logger/default.text.erb
================================================
Notification: <%= event.kind %>


================================================
FILE: app/views/notifier_providers/show.html.erb
================================================

<p>
  <strong>Name:</strong>
  <%= @notifier_provider.name %>
</p>

<p>
  <strong>Kind:</strong>
  <%= @notifier_provider.kind %>
</p>

<p>
  <strong>Settings:</strong>
  <%= @notifier_provider.settings %>
</p>

<%= link_to 'Edit', edit_notifier_provider_path(@notifier_provider) %> |
<%= link_to 'Back', notifier_providers_path %>


================================================
FILE: app/views/notifier_providers/show.json.jbuilder
================================================
json.extract! @notifier_provider, :id, :name, :kind, :settings, :created_at, :updated_at


================================================
FILE: app/views/notifiers/_form.html.erb
================================================
<%= form_for(@notifier) do |f| %>
  <% if @notifier.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@notifier.errors.count, "error") %> prohibited this notifier from being saved:</h2>

      <ul>
      <% @notifier.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :user_id %>
    <%= f.collection_select(:user_id, User.all, :id, :name, { include_blank: true }, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :topic_id %>
    <%= f.collection_select(:topic_id, Topic.all, :id, :name, { include_blank: true }, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :provider_id %>
    <%= f.collection_select(:provider_id, NotifierProvider.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <div class="form-group">
    <%= f.label :settings %>
    <%= f.text_area :settings, value: @notifier.settings.to_yaml, class: 'form-control', rows: 8 %>
  </div>
  <div class="form-group">
    <%= f.label :notify_after_sec %>
    <%= f.number_field :notify_after_sec, min: 5, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :enabled do %>
      <%= f.check_box :enabled %>
      Enabled
    <% end %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/notifiers/edit.html.erb
================================================
<h1>Editing Notifier</h1>

<%= render 'form' %>

<%= link_to 'Show', @notifier %> |
<%= link_to 'Back', notifiers_path %>


================================================
FILE: app/views/notifiers/index.html.erb
================================================

<h1>Listing Notifiers</h1>

<table class="table">
  <thead>
    <tr>
      <th>User</th>
      <th>Topic</th>
      <th>Provider</th>
      <th>Settings</th>
      <th>Notify after sec</th>
      <th>Enabled</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @notifiers.each do |notifier| %>
      <tr>
        <td><%= notifier.user && notifier.user.name %></td>
        <td><%= notifier.topic && notifier.topic.name %></td>
        <td><%= notifier.provider.name %></td>
        <td><%= notifier.settings %></td>
        <td><%= notifier.notify_after_sec %></td>
        <td><%= notifier.enabled %></td>
        <td><%= link_to 'Show', notifier %></td>
        <td><%= link_to 'Edit', edit_notifier_path(notifier) %></td>
        <td><%= link_to 'Destroy', notifier, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Notifier', new_notifier_path %>


================================================
FILE: app/views/notifiers/index.json.jbuilder
================================================
json.array!(@notifiers) do |notifier|
  json.extract! notifier, :id, :settings, :provider_id, :provider, :topic_id, :user_id, :notify_after_sec, :created_at, :updated_at
  json.url notifier_url(notifier, format: :json)
end


================================================
FILE: app/views/notifiers/new.html.erb
================================================
<h1>New Notifier</h1>

<%= render 'form' %>

<%= link_to 'Back', notifiers_path %>


================================================
FILE: app/views/notifiers/show.html.erb
================================================

<p>
  <strong>Provider:</strong>
  <%= @notifier.provider.name %>
</p>

<p>
  <strong>User:</strong>
  <%= @notifier.user && @notifier.user.name %>
</p>

<p>
  <strong>Topic:</strong>
  <%= @notifier.topic && @notifier.topic.name %>
</p>

<p>
  <strong>Settings:</strong>
  <%= @notifier.settings %>
</p>

<p>
  <strong>Notify after sec:</strong>
  <%= @notifier.notify_after_sec %>
</p>

<p>
  <strong>Enabled:</strong>
  <%= @notifier.enabled %>
</p>

<%= link_to 'Edit', edit_notifier_path(@notifier) %> |
<%= link_to 'Back', notifiers_path %>


================================================
FILE: app/views/notifiers/show.json.jbuilder
================================================
json.extract! @notifier, :id, :settings, :provider_id, :provider, :topic_id, :user_id, :notify_after_sec, :created_at, :updated_at


================================================
FILE: app/views/sessions/create.html.erb
================================================
<h1>Sessions#create</h1>
<p>Find me in app/views/sessions/create.html.erb</p>


================================================
FILE: app/views/slack/interactive.html.erb
================================================
<h1>Slack#interactive</h1>
<p>Find me in app/views/slack/interactive.html.erb</p>


================================================
FILE: app/views/topics/_form.html.erb
================================================
<%= form_for(@topic) do |f| %>
  <% if @topic.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@topic.errors.count, "error") %> prohibited this topic from being saved:</h2>

      <ul>
      <% @topic.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :name %>
    <%= f.text_field :name, required: true, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :kind %>
    <%= f.select :kind, nil, {}, class: 'form-control' do %>
      <% Topic.kinds.each do |kind, _| %>
        <%= content_tag(:option, kind.to_s.split('_').map {|s| s.capitalize }.join(' '), value: kind) %>
      <% end %>
    <% end %>
  </div>
  <div class="form-group">
    <%= f.label :escalation_series_id %>
    <%= f.collection_select(:escalation_series_id, EscalationSeries.all, :id, :name, {}, class: 'form-control') %>
  </div>
  <div class="checkbox">
    <%= f.label :enabled do %>
      <%= f.check_box :enabled %>
      Enabled
    <% end %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/topics/edit.html.erb
================================================
<h1>Editing Topic</h1>

<%= render 'form' %>

<%= link_to 'Show', @topic %> |
<%= link_to 'Back', topics_path %>


================================================
FILE: app/views/topics/index.html.erb
================================================

<h1>Listing Topics</h1>

<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Kind</th>
      <th>Escalation Series</th>
      <th>Enabled</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @topics.each do |topic| %>
      <tr>
        <td><%= topic.name %></td>
        <td><%= topic.kind %></td>
        <td><%= topic.escalation_series.name %></td>
        <td><%= topic.enabled ? 'Yes' : 'No' %></td>
        <td><%= link_to 'Show', topic %></td>
        <td><%= link_to 'Edit', edit_topic_path(topic) %></td>
        <td><%= link_to 'Destroy', topic, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Topic', new_topic_path %>


================================================
FILE: app/views/topics/index.json.jbuilder
================================================
json.array!(@topics) do |topic|
  json.extract! topic, :id, :name, :type
  json.url topic_url(topic, format: :json)
end


================================================
FILE: app/views/topics/new.html.erb
================================================
<h1>New Topic</h1>

<%= render 'form' %>

<%= link_to 'Back', topics_path %>


================================================
FILE: app/views/topics/show.html.erb
================================================

<p>
  <strong>Name:</strong>
  <%= @topic.name %>
</p>

<p>
  <strong>Kind:</strong>
  <%= @topic.kind %>
</p>

<p>
  <strong>Escalation Series:</strong>
  <%= @topic.escalation_series.name %>
</p>

<p>
  <strong>Enabled:</strong>
  <%= @topic.enabled ? 'Yes' : 'No' %>
</p>

<p>
  <strong>Mailgun Endpoint:</strong>
  <pre><%= mailgun_topic_url(@topic, format: :json) %></pre>
</p>

<p>
  <strong>Mackerel Endpoint:</strong>
  <pre><%= mackerel_topic_url(@topic, format: :json) %></pre>
</p>

<p>
  <strong>AlertManagerEndpoint:</strong>
  <pre><%= alertmanager_topic_url(@topic, format: :json) %></pre>
</p>

<p>
  <strong>SlackEndpoint:</strong>
  <pre><%= slack_topic_url(@topic, format: :json) %></pre>
</p>

<%= link_to 'Edit', edit_topic_path(@topic) %> |
<%= link_to 'Back', topics_path %>


================================================
FILE: app/views/topics/show.json.jbuilder
================================================
json.extract! @topic, :id, :name, :type, :created_at, :updated_at


================================================
FILE: app/views/users/_form.html.erb
================================================
<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div class="alert alert-danger">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :name %>
    <%= f.text_field :name, required: true, class: 'form-control' %>
  </div>
  <%= f.submit(class: 'btn btn-default') %>
<% end %>


================================================
FILE: app/views/users/edit.html.erb
================================================
<h1>Editing User</h1>

<%= render 'form' %>

<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>


================================================
FILE: app/views/users/index.html.erb
================================================

<h1>Listing Users</h1>

<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Status</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td>
          <%= user.name %>
        </td>
        <td>
          <%- unless user.active -%>
            <span class="label label-default">Deactivated</span>
          <%- end -%>
        </td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        <%- if user.active -%>
          <td><%= link_to 'Deactivate', deactivation_user_path(user), method: :patch, data: { confirm: 'Are you sure?' } %></td>
        <%- else -%>
          <td><%= link_to 'Activate', activation_user_path(user), method: :patch, data: { confirm: 'Are you sure?' } %></td>
        <%- end -%>
      </tr>
    <% end %>
  </tbody>
</table>

<br>


================================================
FILE: app/views/users/index.json.jbuilder
================================================
json.array!(@users) do |user|
  json.extract! user, :id, :name
  json.url user_url(user, format: :json)
end


================================================
FILE: app/views/users/new.html.erb
================================================
<h1>New User</h1>

<%= render 'form' %>

<%= link_to 'Back', users_path %>


================================================
FILE: app/views/users/show.html.erb
================================================

<p>
  <strong>Name:</strong>
  <%= @user.name %>
</p>

<p>
  <strong>Provider:</strong>
  <%= @user.provider %>
</p>

<p>
  <strong>UID:</strong>
  <%= @user.uid %>
</p>

<%= link_to 'Back', users_path %>


================================================
FILE: app/views/users/show.json.jbuilder
================================================
json.extract! @user, :id, :name, :created_at, :updated_at


================================================
FILE: bin/bundle
================================================
#!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
load Gem.bin_path('bundler', 'bundle')


================================================
FILE: bin/rails
================================================
#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'


================================================
FILE: bin/rake
================================================
#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
require_relative '../config/boot'
require 'rake'
Rake.application.run


================================================
FILE: bin/setup
================================================
#!/usr/bin/env ruby
require 'pathname'

# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../',  __FILE__)

Dir.chdir APP_ROOT do
  # This script is a starting point to setup your application.
  # Add necessary setup steps to this file:

  puts "== Installing dependencies =="
  system "gem install bundler --conservative"
  system "bundle check || bundle install"

  # puts "\n== Copying sample files =="
  # unless File.exist?("config/database.yml")
  #   system "cp config/database.yml.sample config/database.yml"
  # end

  puts "\n== Preparing database =="
  system "bin/rake db:setup"

  puts "\n== Removing old logs and tempfiles =="
  system "rm -f log/*"
  system "rm -rf tmp/cache"

  puts "\n== Restarting application server =="
  system "touch tmp/restart.txt"
end


================================================
FILE: bin/spring
================================================
#!/usr/bin/env ruby

# This file loads spring without using Bundler, in order to be fast
# It gets overwritten when you run the `spring binstub` command

unless defined?(Spring)
  require "rubygems"
  require "bundler"

  if match = Bundler.default_lockfile.read.match(/^GEM$.*?^    (?:  )*spring \((.*?)\)$.*?^$/m)
    ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
    ENV["GEM_HOME"] = ""
    Gem.paths = ENV

    gem "spring", match[1]
    require "spring/binstub"
  end
end


================================================
FILE: config/application.rb
================================================
require File.expand_path('../boot', __FILE__)

# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Waker
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    if time_zone = ENV['TIME_ZONE']
      config.time_zone = time_zone
    end

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    # Do not swallow errors in after_commit/after_rollback callbacks.
    config.middleware.use Rack::Health
    config.middleware.use OmniAuth::Builder do
      config = {}
      config[:hd] = ENV['GOOGLE_DOMAIN'] if ENV['GOOGLE_DOMAIN']

      provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
      config.merge(scope: 'userinfo.profile,userinfo.email,calendar',
                   name: 'google_oauth2_with_calendar',
                   access_type: 'offline', approval_prompt: 'force', prompt: 'consent')
    end
  end
end



================================================
FILE: config/boot.rb
================================================
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

require 'bundler/setup' # Set up gems listed in the Gemfile.


================================================
FILE: config/database.yml
================================================
production:
  adapter: mysql2
  database: waker
  encoding: utf8mb4
  username : <%= ENV['MYSQL_USER'] || 'root' %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %>

development:
  adapter: mysql2
  database: waker_development
  encoding: utf8mb4
  username : <%= ENV['MYSQL_USER'] || 'root' %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %>

test:
  adapter: mysql2
  database: waker_test
  encoding: utf8mb4
  username : <%= ENV['MYSQL_USER'] || 'root' %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %>


================================================
FILE: config/environment.rb
================================================
# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!


================================================
FILE: config/environments/development.rb
================================================
Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise an error on page load if there are pending migrations.
  config.active_record.migration_error = :page_load

  # Debug mode disables concatenation and preprocessing of assets.
  # This option may cause significant delays in view rendering with a large
  # number of complex assets.
  config.assets.debug = true

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true

  # Adds additional error checking when serving assets at runtime.
  # Checks for improperly declared sprockets dependencies.
  # Raises helpful error messages.
  config.assets.raise_runtime_errors = true

  # Raises error for missing translations
  # config.action_view.raise_on_missing_translations = true

end

Rails.application.routes.default_url_options[:host] = 'localhost:3000'


================================================
FILE: config/environments/production.rb
================================================
Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Enable Rack::Cache to put a simple HTTP cache in front of your application
  # Add `rack-cache` to your Gemfile before enabling this.
  # For large-scale production use, consider using a caching reverse proxy like
  # NGINX, varnish or squid.
  # config.action_dispatch.rack_cache = true

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?

  # Compress JavaScripts and CSS.
  config.assets.js_compressor = :uglifier
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true

  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  # config.force_ssl = true

  # Use the lowest log level to ensure availability of diagnostic information
  # when problems arise.
  config.log_level = :debug

  # Prepend all log lines with the following tags.
  # config.log_tags = [ :subdomain, :uuid ]

  # Use a different logger for distributed setups.
  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)

  if log_dir = ENV['LOG_DIR']
    config.logger = ::Logger.new(File.expand_path('production.log', log_dir))
  end

  # Use a different cache store in production.
  # config.cache_store = :mem_cache_store

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.action_controller.asset_host = 'http://assets.example.com'

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new

  if ENV["RAILS_LOG_TO_STDOUT"].present?
    logger           = ActiveSupport::Logger.new(STDOUT)
    logger.formatter = config.log_formatter
    config.logger = ActiveSupport::TaggedLogging.new(logger)
  end

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false
end


================================================
FILE: config/environments/test.rb
================================================
Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # The test environment is used exclusively to run your application's
  # test suite. You never need to work with it otherwise. Remember that
  # your test database is "scratch space" for the test suite and is wiped
  # and recreated between test runs. Don't rely on the data there!
  config.cache_classes = true

  # Do not eager load code on boot. This avoids loading your whole application
  # just for the purpose of running a single test. If you are using a tool that
  # preloads Rails for running tests, you may have to set it to true.
  config.eager_load = false

  # Configure static file server for tests with Cache-Control for performance.
  config.serve_static_files   = true
  config.static_cache_control = 'public, max-age=3600'

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Raise exceptions instead of rendering exception templates.
  config.action_dispatch.show_exceptions = false

  # Disable request forgery protection in test environment.
  config.action_controller.allow_forgery_protection = false

  # Tell Action Mailer not to deliver emails to the real world.
  # The :test delivery method accumulates sent emails in the
  # ActionMailer::Base.deliveries array.
  config.action_mailer.delivery_method = :test

  # Randomize the order test cases are executed.
  config.active_support.test_order = :random

  # Print deprecation notices to the stderr.
  config.active_support.deprecation = :stderr

  # Raises error for missing translations
  # config.action_view.raise_on_missing_translations = true
end


================================================
FILE: config/initializers/assets.rb
================================================
# Be sure to restart your server when you modify this file.

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'

# Add additional assets to the asset load path
# Rails.application.config.assets.paths << Emoji.images_path

# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w( search.js )


================================================
FILE: config/initializers/backtrace_silencers.rb
================================================
# Be sure to restart your server when you modify this file.

# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }

# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!


================================================
FILE: config/initializers/cookies_serializer.rb
================================================
# Be sure to restart your server when you modify this file.

Rails.application.config.action_dispatch.cookies_serializer = :json


================================================
FILE: config/initializers/field_with_errors.rb
================================================
Rails.application.configure do
  config.action_view.field_error_proc = lambda do |html_tag, instance|
    %Q{<div class="has-error">#{html_tag}</div>}.html_safe
  end
end


================================================
FILE: config/initializers/filter_parameter_logging.rb
================================================
# Be sure to restart your server when you modify this file.

# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]


================================================
FILE: config/initializers/inflections.rb
================================================
# Be sure to restart your server when you modify this file.

# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.plural /^(ox)$/i, '\1en'
#   inflect.singular /^(ox)en/i, '\1'
#   inflect.irregular 'person', 'people'
#   inflect.uncountable %w( fish sheep )
# end

# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.acronym 'RESTful'
# end


================================================
FILE: config/initializers/mime_types.rb
================================================
# Be sure to restart your server when you modify this file.

# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf


================================================
FILE: config/initializers/omniauth.rb
================================================
OmniAuth.config.logger = Rails.logger


================================================
FILE: config/initializers/session_store.rb
================================================
# Be sure to restart your server when you modify this file.

Rails.application.config.session_store :cookie_store, key: '_waker_session'


================================================
FILE: config/initializers/sidekiq.rb
================================================
namespace = ENV['REDIS_NAMESPACE'] || "waker-#{Rails.env}"
host = ENV['REDIS_HOST'] || 'localhost'
port = ENV['REDIS_PORT'] || 6379

Sidekiq.configure_server do |config|
  config.poll_interval = 5
  config.redis = {url: "redis://#{host}:#{port}", namespace: namespace}
end

Sidekiq.configure_client do |config|
  config.redis = {url: "redis://#{host}:#{port}", namespace: namespace}
end

Sidekiq::Logging.logger = Rails.logger


================================================
FILE: config/initializers/url_options.rb
================================================
if default_host = ENV['DEFAULT_HOST']
  Rails.application.routes.default_url_options[:host] = default_host
end

if default_protocol = ENV['DEFAULT_PROTOCOL']
  Rails.application.routes.default_url_options[:protocol] = default_protocol
end


================================================
FILE: config/initializers/wrap_parameters.rb
================================================
# Be sure to restart your server when you modify this file.

# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.

# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end

# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
#  self.include_root_in_json = true
# end


================================================
FILE: config/locales/en.yml
================================================
# Files in the config/locales directory are used for internationalization
# and are automatically loaded by Rails. If you want to use locales other
# than English, add the necessary files in this directory.
#
# To use the locales, use `I18n.t`:
#
#     I18n.t 'hello'
#
# In views, this is aliased to just `t`:
#
#     <%= t('hello') %>
#
# To use a different locale, set it with `I18n.locale`:
#
#     I18n.locale = :es
#
# This would use the information in config/locales/es.yml.
#
# To learn more, please read the Rails Internationalization guide
# available at http://guides.rubyonrails.org/i18n.html.

en:
  hello: "Hello world"


================================================
FILE: config/routes.rb
================================================
Rails.application.routes.draw do
  post 'slack/interactive'

  get '/auth/:provider/callback', to: 'sessions#create'

  resources :incident_events, only: [] do
    member do
      post 'twilio'
      get 'twilio'
    end
  end

  resources :incidents do
    member do
      get 'acknowledge'
      get 'resolve'
    end

    collection do
      patch 'acknowledge', to: "incidents#bulk_acknowledge"
      patch 'resolve', to: "incidents#bulk_resolve"
    end

    resources :comments
  end

  resources :escalation_series do
    member do
      get 'update_escalations'
    end
  end

  resources :escalations

  resources :shifts

  resources :notifiers

  resources :notifier_providers

  resources :maintenances

  resources :users, only: [:index, :show, :destroy] do
    member do
      patch 'activation'
      patch 'deactivation'
    end
  end

  resources :topics do
    member do
      post 'mailgun'
      post 'mackerel'
      post 'alertmanager'
      post 'slack'
    end
  end

  require 'sidekiq/web'
  mount Sidekiq::Web => '/sidekiq'

  root 'home#index'

  # The priority is based upon order of creation: first created -> highest priority.
  # See how all your routes lay out with "rake routes".

  # You can have the root of your site routed with "root"
  # root 'welcome#index'

  # Example of regular route:
  #   get 'products/:id' => 'catalog#view'

  # Example of named route that can be invoked with purchase_url(id: product.id)
  #   get 'products/:id/purchase' => 'catalog#purchase', as: :purchase

  # Example resource route (maps HTTP verbs to controller actions automatically):
  #   resources :products

  # Example resource route with options:
  #   resources :products do
  #     member do
  #       get 'short'
  #       post 'toggle'
  #     end
  #
  #     collection do
  #       get 'sold'
  #     end
  #   end

  # Example resource route with sub-resources:
  #   resources :products do
  #     resources :comments, :sales
  #     resource :seller
  #   end

  # Example resource route with more complex sub-resources:
  #   resources :products do
  #     resources :comments
  #     resources :sales do
  #       get 'recent', on: :collection
  #     end
  #   end

  # Example resource route with concerns:
  #   concern :toggleable do
  #     post 'toggle'
  #   end
  #   resources :posts, concerns: :toggleable
  #   resources :photos, concerns: :toggleable

  # Example resource route within a namespace:
  #   namespace :admin do
  #     # Directs /admin/products/* to Admin::ProductsController
  #     # (app/controllers/admin/products_controller.rb)
  #     resources :products
  #   end
end


================================================
FILE: config/secrets.yml
================================================
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: d50e30eb49152fcfff420c795f7d0f559934a6497808d4c8a34e83f1ec4807f7c36b4ca47af41bdb4d5a757e9a756f4425c748defb4054f89e63cea1788eb2d3

test:
  secret_key_base: 5206b6e3c7cd04a83e4743c3f0d631e025f196223d8d13742f0bd71c6e1c5d0b0a443398a4536f503d8399c8c7e58e20dadc3553e64bcdb1f4cb7dbc95a7fe0c

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>


================================================
FILE: config.ru
================================================
# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment', __FILE__)
run Rails.application


================================================
FILE: db/migrate/20150120134616_create_topics.rb
================================================
class CreateTopics < ActiveRecord::Migration
  def change
    create_table :topics do |t|
      t.string :name
      t.integer :type

      t.timestamps null: false
    end
  end
end


================================================
FILE: db/migrate/20150120134747_create_users.rb
================================================
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end


================================================
FILE: db/migrate/20150120134905_create_notifiers.rb
================================================
class CreateNotifiers < ActiveRecord::Migration
  def change
    create_table :notifiers do |t|
      t.integer :type
      t.text :settings
      t.integer :notify_after_sec

      t.timestamps null: false
    end
  end
end


================================================
FILE: db/migrate/20150120135017_create_shifts.rb
================================================
class CreateShifts < ActiveRecord::Migration
  def change
    create_table :shifts do |t|
      t.references :user, index: true

      t.timestamps null: false
    end
    add_foreign_key :shifts, :users
  end
end


================================================
FILE: db/migrate/20150120135123_create_escalations.rb
================================================
class CreateEscalations < ActiveRecord::Migration
  def change
    create_table :escalations do |t|
      t.references :escalate_to, index: true
      t.integer :escalate_after_sec

      t.timestamps null: false
    end
    add_foreign_key :escalations, :users, column: 'escalate_to_id'
  end
end


================================================
FILE: db/migrate/20150120135244_create_escalation_series.rb
================================================
class CreateEscalationSeries < ActiveRecord::Migration
  def change
    create_table :escalation_series do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end


================================================
FILE: db/migrate/20150120135351_add_escalation_series_to_escalation.rb
================================================
class AddEscalationSeriesToEscalation < ActiveRecord::Migration
  def change
    add_reference :escalations, :escalation_series, index: true
    add_foreign_key :escalations, :escalation_series
  end
end


================================================
FILE: db/migrate/20150120141627_rename_type_with_kind_of_topic.rb
================================================
class RenameTypeWithKindOfTopic < ActiveRecord::Migration
  def change
    rename_column :topics, :type, :kind
  end
end


================================================
FILE: db/migrate/20150120142452_create_incidents.rb
================================================
class CreateIncidents < ActiveRecord::Migration
  def change
    create_table :incidents do |t|
      t.string :subject
      t.text :description
      t.references :topic, index: true
      t.datetime :occured_at

      t.timestamps null: false
    end
    add_foreign_key :incidents, :topics
  end
end


================================================
FILE: db/migrate/20150120151642_add_escalation_series_to_topic.rb
================================================
class AddEscalationSeriesToTopic < ActiveRecord::Migration
  def change
    add_reference :topics, :escalation_series, index: true
    add_foreign_key :topics, :escalation_series
  end
end


================================================
FILE: db/migrate/20150120154438_add_name_to_shift.rb
================================================
class AddNameToShift < ActiveRecord::Migration
  def change
    add_column :shifts, :name, :string
  end
end


================================================
FILE: db/migrate/20150121150043_add_user_to_notifier.rb
================================================
class AddUserToNotifier < ActiveRecord::Migration
  def change
    add_reference :notifiers, :user, index: true
    add_foreign_key :notifiers, :users
  end
end


================================================
FILE: db/migrate/20150121150857_rename_type_with_kind_of_notifier.rb
================================================
class RenameTypeWithKindOfNotifier < ActiveRecord::Migration
  def change
    rename_column :notifiers, :type, :kind
  end
end


================================================
FILE: db/migrate/20150123132415_remove_shift.rb
================================================
class RemoveShift < ActiveRecord::Migration
  def change
    drop_table :shifts
  end
end


================================================
FILE: db/migrate/20150123150518_add_status_to_incident.rb
================================================
class AddStatusToIncident < ActiveRecord::Migration
  def change
    add_column :incidents, :status, :integer
  end
end


================================================
FILE: db/migrate/20150123150947_create_incident_events.rb
================================================
class CreateIncidentEvents < ActiveRecord::Migration
  def change
    create_table :incident_events do |t|
      t.references :incident, index: true
      t.integer :kind
      t.text :text
      t.references :user_by, index: true

      t.timestamps null: false
    end
    add_foreign_key :incident_events, :incidents
    add_foreign_key :incident_events, :users, column: 'user_by_id'
  end
end


================================================
FILE: db/migrate/20150125050529_create_notifier_providers.rb
================================================
class CreateNotifierProviders < ActiveRecord::Migration
  def change
    create_table :notifier_providers do |t|
      t.string :name
      t.integer :kind
      t.text :settings

      t.timestamps null: false
    end
  end
end


================================================
FILE: db/migrate/20150125050556_add_provider_to_notifier.rb
================================================
class AddProviderToNotifier < ActiveRecord::Migration
  def change
    add_reference :notifiers, :provider, index: true
    add_foreign_key :notifiers, :notifier_providers, column: 'provider_id'
  end
end


================================================
FILE: db/migrate/20150125101901_remove_kind_from_notifier.rb
================================================
class RemoveKindFromNotifier < ActiveRecord::Migration
  def change
    remove_column :notifiers, :kind
  end
end


================================================
FILE: db/migrate/20150127142530_remove_user_by_from_incident_event.rb
================================================
class RemoveUserByFromIncidentEvent < ActiveRecord::Migration
  def change
    remove_foreign_key :incident_events, column: "user_by_id"
    remove_index  :incident_events, :user_by_id
    remove_column :incident_events, :user_by_id
  end
end


================================================
FILE: db/migrate/20150127152127_add_info_to_incident_event.rb
================================================
class AddInfoToIncidentEvent < ActiveRecord::Migration
  def change
    add_column :incident_events, :info, :text
  end
end


================================================
FILE: db/migrate/20150128064248_add_email_to_user.rb
================================================
class AddEmailToUser < ActiveRecord::Migration
  def change
    add_column :users, :email, :string
  end
end


================================================
FILE: db/migrate/20150131120557_add_enable_to_topic.rb
================================================
class AddEnableToTopic < ActiveRecord::Migration
  def change
    add_column :topics, :enable, :boolean
  end
end


================================================
FILE: db/migrate/20150131121143_set_default_of_enable_of_topic_true.rb
================================================
class SetDefaultOfEnableOfTopicTrue < ActiveRecord::Migration
  def change
    change_column :topics, :enable, :boolean, default: true
  end
end


================================================
FILE: db/migrate/20150131122151_rename_enable_with_enabled_of_topic.rb
================================================
class RenameEnableWithEnabledOfTopic < ActiveRecord::Migration
  def change
    rename_column :topics, :enable, :enabled
  end
end


================================================
FILE: db/migrate/20150201033946_add_login_token_to_user.rb
================================================
class AddLoginTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :login_token, :string
  end
end


================================================
FILE: db/migrate/20150202144538_add_token_to_user.rb
================================================
class AddTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :token, :string
  end
end


================================================
FILE: db/migrate/20150202151740_add_refresh_token_to_user.rb
================================================
class AddRefreshTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :refresh_token, :string
  end
end


================================================
FILE: db/migrate/20150202152015_add_settings_to_escalation_series.rb
================================================
class AddSettingsToEscalationSeries < ActiveRecord::Migration
  def change
    add_column :escalation_series, :escalation_series, :text
  end
end


================================================
FILE: db/migrate/20150202155726_add_token_expires_at_to_user.rb
================================================
class AddTokenExpiresAtToUser < ActiveRecord::Migration
  def change
    add_column :users, :token_expires_at, :datetime
  end
end


================================================
FILE: db/migrate/20150203010332_remove_escalation_series_from_escalation_series.rb
================================================
class RemoveEscalationSeriesFromEscalationSeries < ActiveRecord::Migration
  def change
    remove_column :escalation_series, :escalation_series
  end
end


================================================
FILE: db/migrate/20150203010417_add_settings_to_escalation_series_again.rb
================================================
class AddSettingsToEscalationSeriesAgain < ActiveRecord::Migration
  def change
    add_column :escalation_series, :settings, :text
  end
end


================================================
FILE: db/migrate/20150207164010_add_topic_to_notifier.rb
================================================
class AddTopicToNotifier < ActiveRecord::Migration
  def change
    add_reference :notifiers, :topic, index: true
    add_foreign_key :notifiers, :topics
  end
end


================================================
FILE: db/migrate/20150218071007_add_enable_to_notifier.rb
================================================
class AddEnableToNotifier < ActiveRecord::Migration
  def change
    add_column :notifiers, :enabled, :boolean, default: true
  end
end


================================================
FILE: db/migrate/20151117011141_add_active_to_user.rb
================================================
class AddActiveToUser < ActiveRecord::Migration
  def change
    add_column :users, :active, :boolean, default: false
  end
end


================================================
FILE: db/migrate/20151117013824_add_provider_and_uid_to_user.rb
================================================
class AddProviderAndUidToUser < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
  end
end


================================================
FILE: db/migrate/20151118061253_add_credentials_to_user.rb
================================================
class AddCredentialsToUser < ActiveRecord::Migration
  def change
    add_column :users, :credentials, :text
  end
end


================================================
FILE: db/migrate/20151118061938_delete_token_from_user.rb
================================================
class DeleteTokenFromUser < ActiveRecord::Migration
  def change
    User.all.each do |u|
      if u.token
        u.update!(credentials: {
          token: u.token,
          refresh_token: u.refresh_token,
          expires_at: u.token_expires_at.to_i,
          expires: true,
        })
      end
    end

    remove_column :users, :token
    remove_column :users, :token_expires_at
    remove_column :users, :refresh_token
  end
end


================================================
FILE: db/migrate/20160210010310_create_maintenances.rb
================================================
class CreateMaintenances < ActiveRecord::Migration
  def change
    create_table :maintenances do |t|
      t.references :topic, index: true
      t.datetime :start_time
      t.datetime :end_time

      t.timestamps null: false
    end
    add_foreign_key :maintenances, :topics
  end
end


================================================
FILE: db/migrate/20160907123728_create_comments.rb
================================================
class CreateComments < ActiveRecord::Migration
  def change
    create_table "comments", force: :cascade do |t|
      t.integer  "incident_id", limit: 4,   null: false
      t.integer  "user_id",     limit: 4,   null: false
      t.text     "comment",                 null: false
      t.datetime "created_at",              null: false
      t.datetime "updated_at",              null: false
    end
  end
end


================================================
FILE: db/migrate/20160914063913_add_comment_index.rb
================================================
class AddCommentIndex < ActiveRecord::Migration
  def change
    add_index :comments, :incident_id
  end
end


================================================
FILE: db/migrate/20161207045554_add_filter_to_maintenance.rb
================================================
class AddFilterToMaintenance < ActiveRecord::Migration
  def change
    add_column :maintenances, :filter, :string
  end
end


================================================
FILE: db/schema.rb
================================================
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20161207045554) do

  create_table "comments", force: :cascade do |t|
    t.integer  "incident_id", limit: 4,     null: false
    t.integer  "user_id",     limit: 4,     null: false
    t.text     "comment",     limit: 65535, null: false
    t.datetime "created_at",                null: false
    t.datetime "updated_at",                null: false
  end

  add_index "comments", ["incident_id"], name: "index_comments_on_incident_id", using: :btree

  create_table "escalation_series", force: :cascade do |t|
    t.string   "name",       limit: 255
    t.datetime "created_at",               null: false
    t.datetime "updated_at",               null: false
    t.text     "settings",   limit: 65535
  end

  create_table "escalations", force: :cascade do |t|
    t.bigint "escalate_to_id",       limit: 4
    t.integer  "escalate_after_sec",   limit: 4
    t.datetime "created_at",                     null: false
    t.datetime "updated_at",                     null: false
    t.bigint "escalation_series_id", limit: 4
  end

  add_index "escalations", ["escalate_to_id"], name: "index_escalations_on_escalate_to_id", using: :btree
  add_index "escalations", ["escalation_series_id"], name: "index_escalations_on_escalation_series_id", using: :btree

  create_table "incident_events", force: :cascade do |t|
    t.bigint "incident_id", limit: 4
    t.integer  "kind",        limit: 4
    t.text     "text",        limit: 65535
    t.datetime "created_at",                null: false
    t.datetime "updated_at",                null: false
    t.text     "info",        limit: 65535
  end

  add_index "incident_events", ["incident_id"], name: "index_incident_events_on_incident_id", using: :btree

  create_table "incidents", force: :cascade do |t|
    t.string   "subject",     limit: 255
    t.text     "description", limit: 65535
    t.bigint "topic_id",    limit: 4
    t.datetime "occured_at"
    t.datetime "created_at",                null: false
    t.datetime "updated_at",                null: false
    t.integer  "status",      limit: 4
  end

  add_index "incidents", ["topic_id"], name: "index_incidents_on_topic_id", using: :btree

  create_table "maintenances", force: :cascade do |t|
    t.bigint "topic_id",   limit: 4
    t.datetime "start_time"
    t.datetime "end_time"
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
    t.string   "filter",     limit: 255
  end

  add_index "maintenances", ["topic_id"], name: "index_maintenances_on_topic_id", using: :btree

  create_table "notifier_providers", force: :cascade do |t|
    t.string   "name",       limit: 255
    t.integer  "kind",       limit: 4
    t.text     "settings",   limit: 65535
    t.datetime "created_at",               null: false
    t.datetime "updated_at",               null: false
  end

  create_table "notifiers", force: :cascade do |t|
    t.text     "settings",         limit: 65535
    t.integer  "notify_after_sec", limit: 4
    t.datetime "created_at",                                    null: false
    t.datetime "updated_at",                                    null: false
    t.bigint "user_id",          limit: 4
    t.bigint "provider_id",      limit: 4
    t.bigint "topic_id",         limit: 4
    t.boolean  "enabled",                        default: true
  end

  add_index "notifiers", ["provider_id"], name: "index_notifiers_on_provider_id", using: :btree
  add_index "notifiers", ["topic_id"], name: "index_notifiers_on_topic_id", using: :btree
  add_index "notifiers", ["user_id"], name: "index_notifiers_on_user_id", using: :btree

  create_table "topics", force: :cascade do |t|
    t.string   "name",                 limit: 255
    t.integer  "kind",                 limit: 4
    t.datetime "created_at",                                      null: false
    t.datetime "updated_at",                                      null: false
    t.bigint "escalation_series_id", limit: 4
    t.boolean  "enabled",                          default: true
  end

  add_index "topics", ["escalation_series_id"], name: "index_topics_on_escalation_series_id", using: :btree

  create_table "users", force: :cascade do |t|
    t.string   "name",        limit: 255
    t.datetime "created_at",                                null: false
    t.datetime "updated_at",                                null: false
    t.string   "email",       limit: 255
    t.string   "login_token", limit: 255
    t.boolean  "active",                    default: false
    t.string   "provider",    limit: 255
    t.string   "uid",         limit: 255
    t.text     "credentials", limit: 65535
  end

  add_foreign_key "escalations", "escalation_series"
  add_foreign_key "escalations", "users", column: "escalate_to_id"
  add_foreign_key "incident_events", "incidents"
  add_foreign_key "incidents", "topics"
  add_foreign_key "maintenances", "topics"
  add_foreign_key "notifiers", "notifier_providers", column: "provider_id"
  add_foreign_key "notifiers", "topics"
  add_foreign_key "notifiers", "users"
  add_foreign_key "topics", "escalation_series"
end


================================================
FILE: db/seeds.rb
================================================
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
#
# Examples:
#
#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
#   Mayor.create(name: 'Emanuel', city: cities.first)


================================================
FILE: docker/puma.rb
================================================
require 'fileutils'

listen_unix = ENV['LISTEN_UNIX']

if listen_unix
  bind "unix://#{listen_unix}"
end
environment ENV['RAILS_ENV']
port = ENV['PORT'] || 8080
bind "tcp://0.0.0.0:#{port}"


================================================
FILE: docker-compose.yml
================================================
version: '3'
services:
  app:
    build: .
    ports:
      - 5000:5000
    environment:
      DATABASE_URL: 'mysql2://database/waker_development?encoding=utf8mb4&collation=utf8mb4_unicode_ci'
      REDIS_HOST: cache
    depends_on:
      - database
      - cache
  database:
    image: mysql:5
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
      MYSQL_DATABASE: waker_development
  cache:
    image: redis


================================================
FILE: lib/assets/.keep
================================================


================================================
FILE: lib/tasks/.keep
================================================


================================================
FILE: log/.keep
================================================


================================================
FILE: public/404.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>The page you were looking for doesn't exist (404)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  body {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body>
  <!-- This file lives in public/404.html -->
  <div class="dialog">
    <div>
      <h1>The page you were looking for doesn't exist.</h1>
      <p>You may have mistyped the address or the page may have moved.</p>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/422.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>The change you wanted was rejected (422)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  body {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body>
  <!-- This file lives in public/422.html -->
  <div class="dialog">
    <div>
      <h1>The change you wanted was rejected.</h1>
      <p>Maybe you tried to change something you didn't have access to.</p>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/500.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>We're sorry, but something went wrong (500)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  body {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body>
  <!-- This file lives in public/500.html -->
  <div class="dialog">
    <div>
      <h1>We're sorry, but something went wrong.</h1>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/robots.txt
================================================
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
User-agent: *
Disallow: /


================================================
FILE: script/update-escalations-from-google-calendar
================================================
#!/usr/bin/env ruby

require 'optparse'
require 'json'
require 'faraday'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/file_storage'
require 'google/api_client/auth/installed_app'

class Configuration < Struct.new(:client_secret_file, :credential_store_file, :calendar_name, :delimiter, :url, :escalation_series, :login_token)
  def self.from_command_line_options
    self.new.tap do |config|
      opt = OptionParser.new
      opt.on('--client-secret-file VAL') {|v| config.client_secret_file = v }
      opt.on('--credential-store-file VAL') {|v| config.credential_store_file = v }
      opt.on('--calendar-name VAL') {|v| config.calendar_name = v }
      opt.on('--delimiter VAL') {|v| config.delimiter = v }
      opt.on('--url VAL') {|v| config.url = v }
      opt.on('--escalation-series VAL') {|v| config.escalation_series = v }
      opt.on('--login-token VAL') {|v| config.login_token = v }
      opt.parse!(ARGV)

      config.validate!
    end
  end

  def validate!
    members.each do |member|
      unless self[member]
        raise "#{member} is required."
      end
    end
  end
end

class WakerClient
  InvalidResponseError = Class.new(StandardError)

  def initialize(url:, token:)
    @url = url
    @token = token
  end

  def users
    get('/users.json')
  end

  def escalation_series
    get('/escalation_series.json')
  end

  def escalations
    get('/escalations.json')
  end

  def update_escalation(escalation, escalate_to: nil)
    params = {}
    params['escalation[escalate_to_id]'] = escalate_to.id if escalate_to
    update("/escalations/#{escalation.id}.json", params)
  end

  private

  def conn
    @conn ||= Faraday.new(url: @url) do |faraday|
      faraday.request  :url_encoded
      faraday.response :logger if ENV['DEBUG']
      faraday.adapter  Faraday.default_adapter
    end
  end

  def get(path)
    res = conn.get do |req|
      req.url path
      req.headers['X-Login-Token'] = @token
    end

    parse_response(res)
  end

  def update(path, params = {})
    res = conn.put do |req|
      req.url path
      req.params = params
      req.headers['X-Login-Token'] = @token
    end

    parse_response(res)
  end

  def parse_response(res)
    unless 200 <= res.status && res.status < 300
      raise InvalidResponseError, "status #{res.status}\nbody: #{res.body}"
    end

    body = JSON.parse(res.body)
    if body.is_a?(Array)
      body.map {|v| OpenStruct.new(v) }
    elsif body.is_a?(Hash)
      OpenStruct.new(body)
    end
  end
end

def setup_google_client
  client = Google::APIClient.new(application_name: 'Waker',
                                 application_version: '1.0.0')

  file_storage = Google::APIClient::FileStorage.new($config.credential_store_file)
  if file_storage.authorization.nil?
    client_secrets = Google::APIClient::ClientSecrets.load($config.client_secret_file)
    flow = Google::APIClient::InstalledAppFlow.new(
      client_id: client_secrets.client_id,
      client_secret: client_secrets.client_secret,
      scope: ['https://www.googleapis.com/auth/calendar.readonly']
    )
    client.authorization = flow.authorize(file_storage)
  else
    client.authorization = file_storage.authorization
  end

  return client
end

$config = Configuration.from_command_line_options

client = setup_google_client
calendar_api = client.discovered_api('calendar', 'v3')

calendar = client.execute(
  api_method: calendar_api.calendar_list.list,
  parameters: {},
).data.items.find do |cal|
  cal['summary'] == $config.calendar_name
end

events = client.execute(
  api_method: calendar_api.events.list,
  parameters: {
    'calendarId' => calendar['id'],
    'timeMax' => (Time.now + 1).strftime('%FT%T%:z'),
    'timeMin' => (Time.now).strftime('%FT%T%:z'),
    'singleEvents' => true,
  },
).data.items

events.each do |event|
  unless event['end']['dateTime'] && event['start']['dateTime']
    raise "dateTime field is not found (The event may be all-day event)\n#{event}"
  end
end

events.sort! do |a, b|
  a['end']['dateTime'] - a['start']['dateTime'] <=>
    b['end']['dateTime'] - b['start']['dateTime']
end

# shortest event
event = events.first

persons = event['summary'].split($config.delimiter).map(&:strip)

client = WakerClient.new(url: $config.url, token: $config.login_token)

escalation_series = client.escalation_series.find do |series|
  [series.id, series.name].include?($config.escalation_series)
end

escalations = client.escalations.select do |escalation|
  escalation.escalation_series_id == escalation_series.id
end.sort_by do |escalation|
  escalation.escalate_after_sec
end

users = client.users

persons.each_with_index do |name, i|
  user = users.find {|u| u.name == name }
  raise "#{name} user is not found." unless user
  escalation = escalations[i]
  client.update_escalation(escalation, escalate_to: user)
end


================================================
FILE: spec/controllers/slack_controller_spec.rb
================================================
require 'rails_helper'

RSpec.describe SlackController, type: :controller do

  describe "GET interactive" do
    it "raises token verification failed error" do
      expect(get: 'slack/interactive').not_to be_routable
      expect{get :interactive}.to raise_error(RuntimeError, /token verification failed/)
    end
  end

end


================================================
FILE: spec/factories/escalation.rb
================================================
FactoryBot.define do
  factory :escalation do
    association :escalate_to, factory: :user
    escalate_after_sec { 60 }
    escalation_series
  end
end


================================================
FILE: spec/factories/escalation_series.rb
================================================
FactoryBot.define do
  factory :escalation_series do
    name { "Infra" }
  end
end


================================================
FILE: spec/factories/incident.rb
================================================
FactoryBot.define do
  factory :incident do
    id { 1 }
    topic
    subject { "mysql-01 is down" }
    description { "alert" }
  end
end


================================================
FILE: spec/factories/incident_events.rb
================================================
FactoryBot.define do
  factory :incident_event do
    incident_id { 1 }
    kind { :opened }
  end
end


================================================
FILE: spec/factories/maintenances.rb
================================================
FactoryBot.define do
  factory :maintenance do
    topic nil
    start_time { "2016-02-10 10:03:10" }
    end_time { "2016-02-10 10:03:10" }
  end

end


================================================
FILE: spec/factories/notifier.rb
================================================
FactoryBot.define do
  factory :notifier do
    user
    association :provider, factory: :notifier_provider
    notify_after_sec { 60 }
  end
end


================================================
FILE: spec/factories/notifier_provider.rb
================================================
FactoryBot.define do
  factory :notifier_provider do
    name { "Logger" }
    kind { :rails_logger }
  end
end


================================================
FILE: spec/factories/topic.rb
================================================
FactoryBot.define do
  factory :topic do
    name { "Infra" }
    kind { "api" }
    escalation_series
  end
end


================================================
FILE: spec/factories/user.rb
================================================
FactoryBot.define do
  factory :user do
    name { "Ryota Arai" }
  end
end


================================================
FILE: spec/helpers/slack_helper_spec.rb
================================================
require 'rails_helper'

# Specs in this file have access to a helper object that includes
# the SlackHelper. For example:
#
# describe SlackHelper do
#   describe "string concat" do
#     it "concats two strings with spaces" do
#       expect(helper.concat_strings("this","that")).to eq("this that")
#     end
#   end
# end
RSpec.describe SlackHelper, type: :helper do
  pending "add some examples to (or delete) #{__FILE__}"
end


================================================
FILE: spec/models/comment_spec.rb
================================================
require 'rails_helper'

RSpec.describe Comment, type: :model do
  let(:incident) { create(:incident) }
  let(:user) { create(:user) }

  it 'should be valid' do
    comment = Comment.new(
      incident: incident,
      user: user,
      comment: 'Help Me',
    )
    expect(comment).to be_valid
  end

  it 'should be invalid without incident' do
    comment = Comment.new(
      incident: nil,
      user: user,
      comment: 'Help Me',
    )
    expect(comment).not_to be_valid
  end

  it 'should be invalid without user' do
    comment = Comment.new(
      incident: incident,
      user: nil,
      comment: 'Help Me',
    )
    expect(comment).not_to be_valid
  end

  it 'should be invalid without comment' do
    comment = Comment.new(
      incident: incident,
      user: user,
      comment: '',
    )
    expect(comment).not_to be_valid
  end
end


================================================
FILE: spec/models/escalation_series_spec.rb
================================================
require 'rails_helper'
require 'ostruct'
require 'google/api_client'
require 'json'

RSpec.describe EscalationSeries, type: :model do
  describe "after_create enqueue" do
    before do
      # I understand that this is not the recommended method, but it is enough.
      allow_any_instance_of(Google::APIClient).to receive(:execute).and_return(
        OpenStruct.new(
          {
            data: OpenStruct.new({
              items: [{
                'id' => 1,
                'summary' => 'test_calendar'
              }]
            })
          }
        ),
        OpenStruct.new(
          {
              data: OpenStruct.new({
                items: [{
                  'summary' => 'c,b,a',
                  'start' => { 'dateTime' => Time.now },
                  'end' => { 'dateTime' => Time.now + 300 },
                }]
              })
          }
        ),
      )
    end

    it "#update_escalations" do
      users = %w(a b c).map { |n| create(:user, name: n, credentials: { expires_at: Time.now.to_i + 300, token: "dummy", expires: nil, }) }
      series = create(:escalation_series, settings: {
        update_by: 'google_calendar',
        user_as_id: users.first.id,
        calendar: 'test_calendar',
        event_delimiter: ',',
      })
      escalations = users.map { |u| create(:escalation, escalation_series: series, escalate_to: u ) }

      expect(series.update_escalations!).to be_truthy

      users.reverse.each_with_index do |u,i|
        expect(Escalation.find(escalations[i].id).escalate_to.id).to eq(u.id)
      end
    end
  end
end


================================================
FILE: spec/models/incident_spec.rb
================================================
require 'rails_helper'

RSpec.describe Incident, type: :model do
  describe "after_create enqueue" do
    it "creates escalation jobs" do
      series = create(:escalation_series)
      escalation = create(:escalation, escalation_series: series)
      notifier = create(:notifier, user: escalation.escalate_to)
      topic = create(:topic, escalation_series: series)

      incident = nil
      expect {
        incident = create(:incident, topic: topic)
      }.to change(EscalationWorker.jobs, :size).by(1).and change(NotificationWorker.jobs, :size).by(1)

      expect {
        EscalationWorker.drain
      }.to change(incident.events, :count).by(1)

      event = incident.events.last
      expect(event.kind).to eq('escalated')
      expect(event.escalation).to eq(escalation)

      expect(NotificationWorker.jobs.size).to eq(2)
    end
  end
end


================================================
FILE: spec/models/maintenance_spec.rb
================================================
require 'rails_helper'

RSpec.describe Maintenance, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end


================================================
FILE: spec/rails_helper.rb
================================================
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!
end


================================================
FILE: spec/requests/incident_envets_spec.rb
================================================
require 'rails_helper'

RSpec.describe "IncidentEvent", type: :request do
  describe "POST /incident_events/:id/twilio" do
    let(:incident) { create(:incident) }
    let(:event) { create(:incident_event, incident_id: incident.id) }

    describe 'without params[:Digits]' do
      it "works!" do
        post "/incident_events/#{event.id}/twilio"
        expect(response).to have_http_status(200)
      end
    end

    describe 'with params[:Digits]' do
      before do
        post "/incident_events/#{event.id}/twilio", params: { Digits: digit}
        incident.reload
      end

      context '1' do
        let(:digit) { 1 }
        it "acknowledged" do
          expect(response).to have_http_status(200)
          expect(incident.status).to eq 'acknowledged'
        end
      end

      context '2' do
        let(:digit) { 2 }
        it "resolved" do
          expect(response).to have_http_status(200)
          expect(incident.status).to eq 'resolved'
        end
      end
    end
  end
end


================================================
FILE: spec/requests/maintenances_spec.rb
================================================
require 'rails_helper'

RSpec.describe "Maintenances", type: :request do
  describe "GET /maintenances" do
    it "works! (now write some real specs)" do
      get maintenances_path
      expect(response).to have_http_status(200)
    end
  end
end


================================================
FILE: spec/requests/topics_spec.rb
================================================
require "rails_helper"

RSpec.describe "Topics", type: :request do
  describe "POST /topics/1/mailgun" do
    let(:subject) { 'New Alert' }
    let(:body) { "Your server is on fire" }
    it "creates a new incident" do
      topic = create(:topic)
      post mailgun_topic_path(topic, format: :json), params: {
        'subject' => subject,
        'body-plain' => body,
      }
      expect(response).to have_http_status(200)

      incident = Incident.last
      expect(incident.subject).to eq(subject)
      expect(incident.description).to eq(body)
    end
  end

  describe "POST /topics/1/slack" do
    it "creates a new incident" do
      topic = create(:topic)
      post slack_topic_path(topic, format: :json), params: {
        'channel_name' => 'test_channel',
        'user_name' => 'test_user',
        'text' => 'help me',
      }
      expect(response).to have_http_status(200)

      incident = Incident.last
      expect(incident.subject).to eq("escalation from slack,channel name:test_channel help me")
      expect(incident.description).to eq("channel:test_channel user:test_user help me")
    end
  end
end


================================================
FILE: spec/spec_helper.rb
================================================
require 'simplecov'
SimpleCov.start 'rails' do
  add_filter '/vendor/'
  add_filter '/.bundle/'
end

# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
# file to always be loaded, without a need to explicitly require it in any files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
  config.before :suite do
    DatabaseRewinder.clean_all
  end

  config.after :each do
    DatabaseRewinder.clean
  end

  # rspec-expectations config goes here. You can use an alternate
  # assertion/expectation library such as wrong or the stdlib/minitest
  # assertions if you prefer.
  config.expect_with :rspec do |expectations|
    # This option will default to `true` in RSpec 4. It makes the `description`
    # and `failure_message` of custom matchers include text for helper methods
    # defined using `chain`, e.g.:
    # be_bigger_than(2).and_smaller_than(4).description
    #   # => "be bigger than 2 and smaller than 4"
    # ...rather than:
    #   # => "be bigger than 2"
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  # rspec-mocks config goes here. You can use an alternate test double
  # library (such as bogus or mocha) by changing the `mock_with` option here.
  config.mock_with :rspec do |mocks|
    # Prevents you from mocking or stubbing a method that does not exist on
    # a real object. This is generally recommended, and will default to
    # `true` in RSpec 4.
    mocks.verify_partial_doubles = true
  end

  # Run specs in random order to surface order dependencies. If you find an
  # order dependency and want to debug it, you can fix the order by providing
  # the seed, which is printed after each run.
  #     --seed 1234
  config.order = :random

  # Seed global randomization in this process using the `--seed` CLI option.
  # Setting this allows you to use `--seed` to deterministically reproduce
  # test failures related to randomization by passing the same `--seed` value
  # as the one that triggered the failure.
  Kernel.srand config.seed

# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
  # These two settings work together to allow you to limit a spec run
  # to individual examples or groups you care about by tagging them with
  # `:focus` metadata. When nothing is tagged with `:focus`, all examples
  # get run.
  config.filter_run :focus
  config.run_all_when_everything_filtered = true

  # Limits the available syntax to the non-monkey patched syntax that is recommended.
  # For more details, see:
  #   - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
  #   - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
  #   - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
  config.disable_monkey_patching!

  # Many RSpec users commonly either run the entire suite or an individual
  # file, and it's useful to allow more verbose output when running an
  # individual spec file.
  if config.files_to_run.one?
    # Use the documentation formatter for detailed output,
    # unless a formatter has already been configured
    # (e.g. via a command-line flag).
    config.default_formatter = 'doc'
  end

  # Print the 10 slowest examples and example groups at the
  # end of the spec run, to help surface which specs are running
  # particularly slow.
  config.profile_examples = 10
=end
end


================================================
FILE: spec/support/factory_girl.rb
================================================
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods

  config.before(:suite) do
    begin
      FactoryBot.lint
    ensure
      DatabaseRewinder.clean_all
    end
  end
end


================================================
FILE: spec/support/sidekiq.rb
================================================
require 'sidekiq/testing'

RSpec.configure do |config|
  config.before(:each) do
    Sidekiq::Worker.clear_all
  end
end


================================================
FILE: spec/views/slack/interactive.html.erb_spec.rb
================================================
require 'rails_helper'

RSpec.describe "slack/interactive.html.erb", type: :view do
  pending "add some examples to (or delete) #{__FILE__}"
end


================================================
FILE: vendor/assets/javascripts/.keep
================================================


================================================
FILE: vendor/assets/stylesheets/.keep
================================================
Download .txt
gitextract_ao1b4jon/

├── .dockerignore
├── .github/
│   └── workflows/
│       └── rails.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── Dockerfile
├── Gemfile
├── LICENSE.txt
├── Procfile
├── Procfile.docker
├── README.md
├── Rakefile
├── app/
│   ├── assets/
│   │   ├── images/
│   │   │   └── .keep
│   │   ├── javascripts/
│   │   │   ├── application.js
│   │   │   ├── comments.coffee
│   │   │   ├── escalation_series.coffee
│   │   │   ├── escalations.coffee
│   │   │   ├── home.coffee
│   │   │   ├── incident_events.coffee
│   │   │   ├── incidents.coffee
│   │   │   ├── maintenances.coffee
│   │   │   ├── notifier_providers.coffee
│   │   │   ├── notifiers.coffee
│   │   │   ├── sessions.coffee
│   │   │   ├── slack.coffee
│   │   │   ├── topics.coffee
│   │   │   └── users.coffee
│   │   └── stylesheets/
│   │       ├── application.css
│   │       ├── comments.scss
│   │       ├── escalation_series.scss
│   │       ├── escalations.scss
│   │       ├── home.scss
│   │       ├── incident_events.scss
│   │       ├── incidents.scss
│   │       ├── maintenances.scss
│   │       ├── notifier_providers.scss
│   │       ├── notifiers.scss
│   │       ├── scaffolds.scss
│   │       ├── sessions.scss
│   │       ├── slack.scss
│   │       ├── topics.scss
│   │       └── users.scss
│   ├── controllers/
│   │   ├── application_controller.rb
│   │   ├── comments_controller.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── escalation_series_controller.rb
│   │   ├── escalations_controller.rb
│   │   ├── home_controller.rb
│   │   ├── incident_events_controller.rb
│   │   ├── incidents_controller.rb
│   │   ├── maintenances_controller.rb
│   │   ├── notifier_providers_controller.rb
│   │   ├── notifiers_controller.rb
│   │   ├── sessions_controller.rb
│   │   ├── slack_controller.rb
│   │   ├── topics_controller.rb
│   │   └── users_controller.rb
│   ├── helpers/
│   │   ├── application_helper.rb
│   │   ├── comments_helper.rb
│   │   ├── escalation_series_helper.rb
│   │   ├── escalations_helper.rb
│   │   ├── home_helper.rb
│   │   ├── incident_events_helper.rb
│   │   ├── incidents_helper.rb
│   │   ├── maintenances_helper.rb
│   │   ├── notifier_providers_helper.rb
│   │   ├── notifiers_helper.rb
│   │   ├── sessions_helper.rb
│   │   ├── slack_helper.rb
│   │   ├── topics_helper.rb
│   │   └── users_helper.rb
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   ├── .keep
│   │   ├── application_record.rb
│   │   ├── comment.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── escalation.rb
│   │   ├── escalation_series.rb
│   │   ├── escalation_update_worker.rb
│   │   ├── escalation_worker.rb
│   │   ├── incident.rb
│   │   ├── incident_event.rb
│   │   ├── maintenance.rb
│   │   ├── notification_worker.rb
│   │   ├── notifier.rb
│   │   ├── notifier_provider.rb
│   │   ├── topic.rb
│   │   └── user.rb
│   └── views/
│       ├── comments/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── escalation_series/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── escalations/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── home/
│       │   └── index.html.erb
│       ├── incident_events/
│       │   └── twilio.html.erb
│       ├── incidents/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── layouts/
│       │   └── application.html.erb
│       ├── maintenances/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── notifier_providers/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── hipchat/
│       │   │   ├── acknowledged.text.erb
│       │   │   ├── escalated.text.erb
│       │   │   ├── opened.text.erb
│       │   │   └── resolved.text.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── mailgun/
│       │   │   ├── default.html.erb
│       │   │   └── default.text.erb
│       │   ├── new.html.erb
│       │   ├── rails_logger/
│       │   │   └── default.text.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── notifiers/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       ├── sessions/
│       │   └── create.html.erb
│       ├── slack/
│       │   └── interactive.html.erb
│       ├── topics/
│       │   ├── _form.html.erb
│       │   ├── edit.html.erb
│       │   ├── index.html.erb
│       │   ├── index.json.jbuilder
│       │   ├── new.html.erb
│       │   ├── show.html.erb
│       │   └── show.json.jbuilder
│       └── users/
│           ├── _form.html.erb
│           ├── edit.html.erb
│           ├── index.html.erb
│           ├── index.json.jbuilder
│           ├── new.html.erb
│           ├── show.html.erb
│           └── show.json.jbuilder
├── bin/
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── spring
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── initializers/
│   │   ├── assets.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── cookies_serializer.rb
│   │   ├── field_with_errors.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── omniauth.rb
│   │   ├── session_store.rb
│   │   ├── sidekiq.rb
│   │   ├── url_options.rb
│   │   └── wrap_parameters.rb
│   ├── locales/
│   │   └── en.yml
│   ├── routes.rb
│   └── secrets.yml
├── config.ru
├── db/
│   ├── migrate/
│   │   ├── 20150120134616_create_topics.rb
│   │   ├── 20150120134747_create_users.rb
│   │   ├── 20150120134905_create_notifiers.rb
│   │   ├── 20150120135017_create_shifts.rb
│   │   ├── 20150120135123_create_escalations.rb
│   │   ├── 20150120135244_create_escalation_series.rb
│   │   ├── 20150120135351_add_escalation_series_to_escalation.rb
│   │   ├── 20150120141627_rename_type_with_kind_of_topic.rb
│   │   ├── 20150120142452_create_incidents.rb
│   │   ├── 20150120151642_add_escalation_series_to_topic.rb
│   │   ├── 20150120154438_add_name_to_shift.rb
│   │   ├── 20150121150043_add_user_to_notifier.rb
│   │   ├── 20150121150857_rename_type_with_kind_of_notifier.rb
│   │   ├── 20150123132415_remove_shift.rb
│   │   ├── 20150123150518_add_status_to_incident.rb
│   │   ├── 20150123150947_create_incident_events.rb
│   │   ├── 20150125050529_create_notifier_providers.rb
│   │   ├── 20150125050556_add_provider_to_notifier.rb
│   │   ├── 20150125101901_remove_kind_from_notifier.rb
│   │   ├── 20150127142530_remove_user_by_from_incident_event.rb
│   │   ├── 20150127152127_add_info_to_incident_event.rb
│   │   ├── 20150128064248_add_email_to_user.rb
│   │   ├── 20150131120557_add_enable_to_topic.rb
│   │   ├── 20150131121143_set_default_of_enable_of_topic_true.rb
│   │   ├── 20150131122151_rename_enable_with_enabled_of_topic.rb
│   │   ├── 20150201033946_add_login_token_to_user.rb
│   │   ├── 20150202144538_add_token_to_user.rb
│   │   ├── 20150202151740_add_refresh_token_to_user.rb
│   │   ├── 20150202152015_add_settings_to_escalation_series.rb
│   │   ├── 20150202155726_add_token_expires_at_to_user.rb
│   │   ├── 20150203010332_remove_escalation_series_from_escalation_series.rb
│   │   ├── 20150203010417_add_settings_to_escalation_series_again.rb
│   │   ├── 20150207164010_add_topic_to_notifier.rb
│   │   ├── 20150218071007_add_enable_to_notifier.rb
│   │   ├── 20151117011141_add_active_to_user.rb
│   │   ├── 20151117013824_add_provider_and_uid_to_user.rb
│   │   ├── 20151118061253_add_credentials_to_user.rb
│   │   ├── 20151118061938_delete_token_from_user.rb
│   │   ├── 20160210010310_create_maintenances.rb
│   │   ├── 20160907123728_create_comments.rb
│   │   ├── 20160914063913_add_comment_index.rb
│   │   └── 20161207045554_add_filter_to_maintenance.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker/
│   └── puma.rb
├── docker-compose.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   └── tasks/
│       └── .keep
├── log/
│   └── .keep
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   └── robots.txt
├── script/
│   └── update-escalations-from-google-calendar
├── spec/
│   ├── controllers/
│   │   └── slack_controller_spec.rb
│   ├── factories/
│   │   ├── escalation.rb
│   │   ├── escalation_series.rb
│   │   ├── incident.rb
│   │   ├── incident_events.rb
│   │   ├── maintenances.rb
│   │   ├── notifier.rb
│   │   ├── notifier_provider.rb
│   │   ├── topic.rb
│   │   └── user.rb
│   ├── helpers/
│   │   └── slack_helper_spec.rb
│   ├── models/
│   │   ├── comment_spec.rb
│   │   ├── escalation_series_spec.rb
│   │   ├── incident_spec.rb
│   │   └── maintenance_spec.rb
│   ├── rails_helper.rb
│   ├── requests/
│   │   ├── incident_envets_spec.rb
│   │   ├── maintenances_spec.rb
│   │   └── topics_spec.rb
│   ├── spec_helper.rb
│   ├── support/
│   │   ├── factory_girl.rb
│   │   └── sidekiq.rb
│   └── views/
│       └── slack/
│           └── interactive.html.erb_spec.rb
└── vendor/
    └── assets/
        ├── javascripts/
        │   └── .keep
        └── stylesheets/
            └── .keep
Download .txt
SYMBOL INDEX (338 symbols across 85 files)

FILE: app/controllers/application_controller.rb
  class ApplicationController (line 1) | class ApplicationController < ActionController::Base
    method login_required (line 11) | def login_required
    method current_user= (line 25) | def current_user=(user)
    method current_user (line 29) | def current_user

FILE: app/controllers/comments_controller.rb
  class CommentsController (line 1) | class CommentsController < ApplicationController
    method index (line 7) | def index
    method show (line 13) | def show
    method new (line 17) | def new
    method edit (line 22) | def edit
    method create (line 27) | def create
    method update (line 44) | def update
    method destroy (line 58) | def destroy
    method set_comment (line 68) | def set_comment
    method comment_params (line 73) | def comment_params
    method set_incident (line 77) | def set_incident

FILE: app/controllers/escalation_series_controller.rb
  class EscalationSeriesController (line 1) | class EscalationSeriesController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 21) | def edit
    method create (line 26) | def create
    method update (line 42) | def update
    method destroy (line 56) | def destroy
    method update_escalations (line 64) | def update_escalations
    method set_escalation_series (line 70) | def set_escalation_series
    method escalation_series_params (line 75) | def escalation_series_params

FILE: app/controllers/escalations_controller.rb
  class EscalationsController (line 1) | class EscalationsController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 21) | def edit
    method create (line 26) | def create
    method update (line 42) | def update
    method destroy (line 56) | def destroy
    method set_escalation (line 66) | def set_escalation
    method escalation_params (line 71) | def escalation_params

FILE: app/controllers/home_controller.rb
  class HomeController (line 1) | class HomeController < ApplicationController
    method index (line 2) | def index

FILE: app/controllers/incident_events_controller.rb
  class IncidentEventsController (line 3) | class IncidentEventsController < ApplicationController
    method twilio (line 6) | def twilio

FILE: app/controllers/incidents_controller.rb
  class IncidentsController (line 1) | class IncidentsController < ApplicationController
    method index (line 9) | def index
    method show (line 16) | def show
    method new (line 20) | def new
    method edit (line 25) | def edit
    method create (line 30) | def create
    method update (line 46) | def update
    method bulk_acknowledge (line 58) | def bulk_acknowledge
    method bulk_resolve (line 66) | def bulk_resolve
    method destroy (line 76) | def destroy
    method acknowledge (line 84) | def acknowledge
    method resolve (line 96) | def resolve
    method set_incident (line 110) | def set_incident
    method set_incidents (line 114) | def set_incidents
    method set_visible_statuses (line 128) | def set_visible_statuses
    method set_visible_topic (line 144) | def set_visible_topic
    method incident_params (line 161) | def incident_params
    method ensure_hash (line 165) | def ensure_hash

FILE: app/controllers/maintenances_controller.rb
  class MaintenancesController (line 1) | class MaintenancesController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 25) | def edit
    method create (line 30) | def create
    method update (line 46) | def update
    method destroy (line 60) | def destroy
    method set_maintenance (line 70) | def set_maintenance
    method maintenance_params (line 75) | def maintenance_params

FILE: app/controllers/notifier_providers_controller.rb
  class NotifierProvidersController (line 1) | class NotifierProvidersController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 21) | def edit
    method create (line 26) | def create
    method update (line 42) | def update
    method destroy (line 56) | def destroy
    method set_notifier_provider (line 66) | def set_notifier_provider
    method notifier_provider_params (line 71) | def notifier_provider_params

FILE: app/controllers/notifiers_controller.rb
  class NotifiersController (line 1) | class NotifiersController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 21) | def edit
    method create (line 26) | def create
    method update (line 42) | def update
    method destroy (line 56) | def destroy
    method set_notifier (line 66) | def set_notifier
    method notifier_params (line 71) | def notifier_params

FILE: app/controllers/sessions_controller.rb
  class SessionsController (line 1) | class SessionsController < ApplicationController
    method create (line 4) | def create
    method auth_hash (line 13) | def auth_hash

FILE: app/controllers/slack_controller.rb
  class SlackController (line 1) | class SlackController < ApplicationController
    method interactive (line 4) | def interactive
    method payload (line 33) | def payload
    method verify! (line 37) | def verify!
    method incident_id (line 53) | def incident_id
    method incident (line 57) | def incident

FILE: app/controllers/topics_controller.rb
  class TopicsController (line 1) | class TopicsController < ApplicationController
    method index (line 7) | def index
    method show (line 13) | def show
    method new (line 17) | def new
    method edit (line 22) | def edit
    method create (line 27) | def create
    method update (line 43) | def update
    method destroy (line 57) | def destroy
    method mailgun (line 66) | def mailgun
    method mackerel (line 92) | def mackerel
    method alertmanager (line 124) | def alertmanager
    method slack (line 149) | def slack
    method set_topic (line 175) | def set_topic
    method topic_params (line 180) | def topic_params

FILE: app/controllers/users_controller.rb
  class UsersController (line 1) | class UsersController < ApplicationController
    method index (line 6) | def index
    method show (line 12) | def show
    method new (line 16) | def new
    method edit (line 21) | def edit
    method create (line 26) | def create
    method update (line 42) | def update
    method destroy (line 56) | def destroy
    method activation (line 65) | def activation
    method deactivation (line 73) | def deactivation
    method set_user (line 82) | def set_user
    method user_params (line 87) | def user_params

FILE: app/helpers/application_helper.rb
  type ApplicationHelper (line 1) | module ApplicationHelper

FILE: app/helpers/comments_helper.rb
  type CommentsHelper (line 1) | module CommentsHelper

FILE: app/helpers/escalation_series_helper.rb
  type EscalationSeriesHelper (line 1) | module EscalationSeriesHelper

FILE: app/helpers/escalations_helper.rb
  type EscalationsHelper (line 1) | module EscalationsHelper

FILE: app/helpers/home_helper.rb
  type HomeHelper (line 1) | module HomeHelper

FILE: app/helpers/incident_events_helper.rb
  type IncidentEventsHelper (line 1) | module IncidentEventsHelper

FILE: app/helpers/incidents_helper.rb
  type IncidentsHelper (line 1) | module IncidentsHelper

FILE: app/helpers/maintenances_helper.rb
  type MaintenancesHelper (line 1) | module MaintenancesHelper

FILE: app/helpers/notifier_providers_helper.rb
  type NotifierProvidersHelper (line 1) | module NotifierProvidersHelper

FILE: app/helpers/notifiers_helper.rb
  type NotifiersHelper (line 1) | module NotifiersHelper

FILE: app/helpers/sessions_helper.rb
  type SessionsHelper (line 1) | module SessionsHelper

FILE: app/helpers/slack_helper.rb
  type SlackHelper (line 1) | module SlackHelper

FILE: app/helpers/topics_helper.rb
  type TopicsHelper (line 1) | module TopicsHelper

FILE: app/helpers/users_helper.rb
  type UsersHelper (line 1) | module UsersHelper

FILE: app/models/application_record.rb
  class ApplicationRecord (line 2) | class ApplicationRecord < ActiveRecord::Base

FILE: app/models/comment.rb
  class Comment (line 1) | class Comment < ApplicationRecord

FILE: app/models/escalation.rb
  class Escalation (line 1) | class Escalation < ApplicationRecord

FILE: app/models/escalation_series.rb
  class EscalationSeries (line 1) | class EscalationSeries < ApplicationRecord
    method set_defaults (line 10) | def set_defaults
    method update_escalations! (line 14) | def update_escalations!
    class EscalationUpdater (line 27) | class EscalationUpdater
      method initialize (line 28) | def initialize(series)
      method update! (line 32) | def update!
      method settings (line 38) | def settings
    class GoogleCalendarEscalationUpdater (line 43) | class GoogleCalendarEscalationUpdater < EscalationUpdater
      method initialize (line 44) | def initialize(*)
      method update! (line 49) | def update!
      method user_as (line 129) | def user_as
      method calendar_name (line 133) | def calendar_name
      method event_delimiter (line 137) | def event_delimiter

FILE: app/models/escalation_update_worker.rb
  class EscalationUpdateWorker (line 1) | class EscalationUpdateWorker
    method perform (line 4) | def perform
    method handle_series (line 14) | def handle_series(series)

FILE: app/models/escalation_worker.rb
  class EscalationWorker (line 1) | class EscalationWorker
    method enqueue (line 4) | def self.enqueue(incident, escalation)
    method perform (line 9) | def perform(incident_id, escalation_id)

FILE: app/models/incident.rb
  class Incident (line 3) | class Incident < ApplicationRecord
    method acknowledge! (line 19) | def acknowledge!
    method resolve! (line 27) | def resolve!
    method confirmation_hash (line 35) | def confirmation_hash
    method set_defaults (line 41) | def set_defaults
    method enqueue (line 46) | def enqueue

FILE: app/models/incident_event.rb
  class IncidentEvent (line 1) | class IncidentEvent < ApplicationRecord
    method set_defaults (line 13) | def set_defaults
    method notify (line 17) | def notify
    method escalated_to (line 26) | def escalated_to
    method escalation (line 30) | def escalation
    method notifier (line 34) | def notifier
    method event (line 38) | def event

FILE: app/models/maintenance.rb
  class Maintenance (line 1) | class Maintenance < ApplicationRecord
    method filter_regexp (line 8) | def filter_regexp

FILE: app/models/notification_worker.rb
  class NotificationWorker (line 1) | class NotificationWorker
    method enqueue (line 4) | def self.enqueue(event:, notifier:)
    method perform (line 10) | def perform(event_id, notifier_id)

FILE: app/models/notifier.rb
  class Notifier (line 1) | class Notifier < ApplicationRecord
    method set_defaults (line 12) | def set_defaults
    method notify (line 16) | def notify(event)
    method notify_immediately (line 20) | def notify_immediately(event)

FILE: app/models/notifier_provider.rb
  class NotifierProvider (line 1) | class NotifierProvider < ApplicationRecord
    method set_defaults (line 9) | def set_defaults
    method concrete_class (line 13) | def concrete_class
    method notify (line 17) | def notify(event:, notifier:)
    class ConcreteProvider (line 21) | class ConcreteProvider
      method initialize (line 22) | def initialize(provider:, notifier:, event:)
      method notify (line 28) | def notify
      method _notify (line 44) | def _notify
      method settings (line 48) | def settings
      method skip? (line 52) | def skip?
      method skip_due_to_status_of_incident? (line 66) | def skip_due_to_status_of_incident?
      method skip_due_to_or_conditions? (line 74) | def skip_due_to_or_conditions?
      method all_events (line 113) | def all_events
      method target_events (line 117) | def target_events
      method body (line 122) | def body(formats: [:text])
      method kind_of_event (line 140) | def kind_of_event
    class FileConcreteProvider (line 149) | class FileConcreteProvider < ConcreteProvider
      method _notify (line 150) | def _notify
    class RailsLoggerConcreteProvider (line 154) | class RailsLoggerConcreteProvider < ConcreteProvider
      method _notify (line 155) | def _notify
      method target_events (line 159) | def target_events
    class HipchatConcreteProvider (line 164) | class HipchatConcreteProvider < ConcreteProvider
      method _notify (line 165) | def _notify
      method api_token (line 183) | def api_token
      method room (line 187) | def room
      method api_version (line 191) | def api_version
      method notify? (line 202) | def notify?
      method target_events (line 206) | def target_events
    class MailgunConcreteProvider (line 211) | class MailgunConcreteProvider < ConcreteProvider
      method _notify (line 212) | def _notify
      method api_key (line 234) | def api_key
      method domain (line 238) | def domain
      method from (line 242) | def from
      method to (line 246) | def to
      method target_events (line 250) | def target_events
    class TwilioConcreteProvider (line 255) | class TwilioConcreteProvider < ConcreteProvider
      method _notify (line 256) | def _notify
      method account_sid (line 272) | def account_sid
      method auth_token (line 276) | def auth_token
      method from (line 280) | def from
      method to (line 284) | def to
      method target_events (line 288) | def target_events
      method basic_auth_user (line 292) | def basic_auth_user
      method basic_auth_password (line 296) | def basic_auth_password
    class SlackConcreteProvider (line 301) | class SlackConcreteProvider < ConcreteProvider
      method _notify (line 302) | def _notify
      method webhook_url (line 393) | def webhook_url
      method channel (line 397) | def channel
      method buttons_enabled? (line 401) | def buttons_enabled?
      method target_events (line 405) | def target_events
    class DatadogConcreteProvider (line 410) | class DatadogConcreteProvider < ConcreteProvider
      method _notify (line 411) | def _notify
      method api_key (line 428) | def api_key
      method app_key (line 432) | def app_key
      method alert_type (line 436) | def alert_type
      method tags (line 440) | def tags
      method source_type_name (line 444) | def source_type_name
      method target_events (line 448) | def target_events
    class SnsConcreteProvider (line 453) | class SnsConcreteProvider < ConcreteProvider
      method _notify (line 454) | def _notify
      method topic_arn (line 479) | def topic_arn
      method region (line 483) | def region
      method access_key_id (line 487) | def access_key_id
      method secret_access_key (line 491) | def secret_access_key
      method target_events (line 495) | def target_events

FILE: app/models/topic.rb
  class Topic (line 1) | class Topic < ApplicationRecord
    method in_maintenance? (line 10) | def in_maintenance?(*bodies)

FILE: app/models/user.rb
  class User (line 3) | class User < ApplicationRecord
    method find_or_create_from_auth_hash (line 14) | def self.find_or_create_from_auth_hash(auth_hash)
    method update_credentials_from_auth_hash (line 28) | def update_credentials_from_auth_hash(auth_hash)
    method set_defaults (line 34) | def set_defaults

FILE: config/application.rb
  type Waker (line 17) | module Waker
    class Application (line 18) | class Application < Rails::Application

FILE: db/migrate/20150120134616_create_topics.rb
  class CreateTopics (line 1) | class CreateTopics < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120134747_create_users.rb
  class CreateUsers (line 1) | class CreateUsers < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120134905_create_notifiers.rb
  class CreateNotifiers (line 1) | class CreateNotifiers < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120135017_create_shifts.rb
  class CreateShifts (line 1) | class CreateShifts < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120135123_create_escalations.rb
  class CreateEscalations (line 1) | class CreateEscalations < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120135244_create_escalation_series.rb
  class CreateEscalationSeries (line 1) | class CreateEscalationSeries < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120135351_add_escalation_series_to_escalation.rb
  class AddEscalationSeriesToEscalation (line 1) | class AddEscalationSeriesToEscalation < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120141627_rename_type_with_kind_of_topic.rb
  class RenameTypeWithKindOfTopic (line 1) | class RenameTypeWithKindOfTopic < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120142452_create_incidents.rb
  class CreateIncidents (line 1) | class CreateIncidents < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120151642_add_escalation_series_to_topic.rb
  class AddEscalationSeriesToTopic (line 1) | class AddEscalationSeriesToTopic < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150120154438_add_name_to_shift.rb
  class AddNameToShift (line 1) | class AddNameToShift < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150121150043_add_user_to_notifier.rb
  class AddUserToNotifier (line 1) | class AddUserToNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150121150857_rename_type_with_kind_of_notifier.rb
  class RenameTypeWithKindOfNotifier (line 1) | class RenameTypeWithKindOfNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150123132415_remove_shift.rb
  class RemoveShift (line 1) | class RemoveShift < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150123150518_add_status_to_incident.rb
  class AddStatusToIncident (line 1) | class AddStatusToIncident < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150123150947_create_incident_events.rb
  class CreateIncidentEvents (line 1) | class CreateIncidentEvents < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150125050529_create_notifier_providers.rb
  class CreateNotifierProviders (line 1) | class CreateNotifierProviders < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150125050556_add_provider_to_notifier.rb
  class AddProviderToNotifier (line 1) | class AddProviderToNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150125101901_remove_kind_from_notifier.rb
  class RemoveKindFromNotifier (line 1) | class RemoveKindFromNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150127142530_remove_user_by_from_incident_event.rb
  class RemoveUserByFromIncidentEvent (line 1) | class RemoveUserByFromIncidentEvent < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150127152127_add_info_to_incident_event.rb
  class AddInfoToIncidentEvent (line 1) | class AddInfoToIncidentEvent < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150128064248_add_email_to_user.rb
  class AddEmailToUser (line 1) | class AddEmailToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150131120557_add_enable_to_topic.rb
  class AddEnableToTopic (line 1) | class AddEnableToTopic < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150131121143_set_default_of_enable_of_topic_true.rb
  class SetDefaultOfEnableOfTopicTrue (line 1) | class SetDefaultOfEnableOfTopicTrue < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150131122151_rename_enable_with_enabled_of_topic.rb
  class RenameEnableWithEnabledOfTopic (line 1) | class RenameEnableWithEnabledOfTopic < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150201033946_add_login_token_to_user.rb
  class AddLoginTokenToUser (line 1) | class AddLoginTokenToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150202144538_add_token_to_user.rb
  class AddTokenToUser (line 1) | class AddTokenToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150202151740_add_refresh_token_to_user.rb
  class AddRefreshTokenToUser (line 1) | class AddRefreshTokenToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150202152015_add_settings_to_escalation_series.rb
  class AddSettingsToEscalationSeries (line 1) | class AddSettingsToEscalationSeries < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150202155726_add_token_expires_at_to_user.rb
  class AddTokenExpiresAtToUser (line 1) | class AddTokenExpiresAtToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150203010332_remove_escalation_series_from_escalation_series.rb
  class RemoveEscalationSeriesFromEscalationSeries (line 1) | class RemoveEscalationSeriesFromEscalationSeries < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150203010417_add_settings_to_escalation_series_again.rb
  class AddSettingsToEscalationSeriesAgain (line 1) | class AddSettingsToEscalationSeriesAgain < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150207164010_add_topic_to_notifier.rb
  class AddTopicToNotifier (line 1) | class AddTopicToNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20150218071007_add_enable_to_notifier.rb
  class AddEnableToNotifier (line 1) | class AddEnableToNotifier < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20151117011141_add_active_to_user.rb
  class AddActiveToUser (line 1) | class AddActiveToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20151117013824_add_provider_and_uid_to_user.rb
  class AddProviderAndUidToUser (line 1) | class AddProviderAndUidToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20151118061253_add_credentials_to_user.rb
  class AddCredentialsToUser (line 1) | class AddCredentialsToUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20151118061938_delete_token_from_user.rb
  class DeleteTokenFromUser (line 1) | class DeleteTokenFromUser < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20160210010310_create_maintenances.rb
  class CreateMaintenances (line 1) | class CreateMaintenances < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20160907123728_create_comments.rb
  class CreateComments (line 1) | class CreateComments < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20160914063913_add_comment_index.rb
  class AddCommentIndex (line 1) | class AddCommentIndex < ActiveRecord::Migration
    method change (line 2) | def change

FILE: db/migrate/20161207045554_add_filter_to_maintenance.rb
  class AddFilterToMaintenance (line 1) | class AddFilterToMaintenance < ActiveRecord::Migration
    method change (line 2) | def change
Condensed preview — 270 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (184K chars).
[
  {
    "path": ".dockerignore",
    "chars": 39,
    "preview": "logs/*\ndoc/*\nvendor/*\ncoverage/*\ntmp/*\n"
  },
  {
    "path": ".github/workflows/rails.yml",
    "chars": 888,
    "preview": "name: Rails\n\non: [push]\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    services:\n      mysql:\n        image: mysql:5.7\n"
  },
  {
    "path": ".gitignore",
    "chars": 494,
    "preview": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring t"
  },
  {
    "path": ".rspec",
    "chars": 30,
    "preview": "--color\n--require spec_helper\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 955,
    "preview": "AllCops:\n  TargetRubyVersion: 2.4\n  DisplayCopNames: true\n  DisabledByDefault: true\n  Exclude:\n    - 'db/**/*'\n    - 've"
  },
  {
    "path": "Dockerfile",
    "chars": 376,
    "preview": "FROM ruby:2.6.5\n\nRUN apt update -qqy && apt -qqy install nodejs\n\nWORKDIR /tmp\nADD Gemfile* /tmp/\n\nRUN gem install bundle"
  },
  {
    "path": "Gemfile",
    "chars": 1812,
    "preview": "source 'https://rubygems.org'\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '5.2.4.1'\n\n#"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1082,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014-2015 Ryota Arai\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "Procfile",
    "chars": 162,
    "preview": "web: bin/rails s\nworker: bundle exec sidekiq\nupdate_escalations: while true; do bundle exec rails runner 'EscalationUpda"
  },
  {
    "path": "Procfile.docker",
    "chars": 173,
    "preview": "web: puma -C docker/puma.rb\nworker: bundle exec sidekiq\nupdate_escalations: while true; do bundle exec rails runner 'Esc"
  },
  {
    "path": "README.md",
    "chars": 3532,
    "preview": "# Waker [![Build Status](https://travis-ci.org/ryotarai/waker.svg?branch=master)](https://travis-ci.org/ryotarai/waker)\n"
  },
  {
    "path": "Rakefile",
    "chars": 249,
    "preview": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they wil"
  },
  {
    "path": "app/assets/images/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/assets/javascripts/application.js",
    "chars": 644,
    "preview": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//"
  },
  {
    "path": "app/assets/javascripts/comments.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/escalation_series.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/escalations.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/home.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/incident_events.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/incidents.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/maintenances.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/notifier_providers.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/notifiers.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/sessions.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/slack.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/topics.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/javascripts/users.coffee",
    "chars": 211,
    "preview": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be avai"
  },
  {
    "path": "app/assets/stylesheets/application.css",
    "chars": 686,
    "preview": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below"
  },
  {
    "path": "app/assets/stylesheets/comments.scss",
    "chars": 179,
    "preview": "// Place all the styles related to the comments controller here.\n// They will automatically be included in application.c"
  },
  {
    "path": "app/assets/stylesheets/escalation_series.scss",
    "chars": 187,
    "preview": "// Place all the styles related to the EscalationSeries controller here.\n// They will automatically be included in appli"
  },
  {
    "path": "app/assets/stylesheets/escalations.scss",
    "chars": 182,
    "preview": "// Place all the styles related to the escalations controller here.\n// They will automatically be included in applicatio"
  },
  {
    "path": "app/assets/stylesheets/home.scss",
    "chars": 175,
    "preview": "// Place all the styles related to the home controller here.\n// They will automatically be included in application.css.\n"
  },
  {
    "path": "app/assets/stylesheets/incident_events.scss",
    "chars": 186,
    "preview": "// Place all the styles related to the incident_events controller here.\n// They will automatically be included in applic"
  },
  {
    "path": "app/assets/stylesheets/incidents.scss",
    "chars": 180,
    "preview": "// Place all the styles related to the incidents controller here.\n// They will automatically be included in application."
  },
  {
    "path": "app/assets/stylesheets/maintenances.scss",
    "chars": 183,
    "preview": "// Place all the styles related to the maintenances controller here.\n// They will automatically be included in applicati"
  },
  {
    "path": "app/assets/stylesheets/notifier_providers.scss",
    "chars": 188,
    "preview": "// Place all the styles related to the NotifierProviders controller here.\n// They will automatically be included in appl"
  },
  {
    "path": "app/assets/stylesheets/notifiers.scss",
    "chars": 180,
    "preview": "// Place all the styles related to the notifiers controller here.\n// They will automatically be included in application."
  },
  {
    "path": "app/assets/stylesheets/scaffolds.scss",
    "chars": 1027,
    "preview": "body {\n  background-color: #fff;\n  color: #333;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;"
  },
  {
    "path": "app/assets/stylesheets/sessions.scss",
    "chars": 179,
    "preview": "// Place all the styles related to the sessions controller here.\n// They will automatically be included in application.c"
  },
  {
    "path": "app/assets/stylesheets/slack.scss",
    "chars": 176,
    "preview": "// Place all the styles related to the slack controller here.\n// They will automatically be included in application.css."
  },
  {
    "path": "app/assets/stylesheets/topics.scss",
    "chars": 177,
    "preview": "// Place all the styles related to the topics controller here.\n// They will automatically be included in application.css"
  },
  {
    "path": "app/assets/stylesheets/users.scss",
    "chars": 176,
    "preview": "// Place all the styles related to the users controller here.\n// They will automatically be included in application.css."
  },
  {
    "path": "app/controllers/application_controller.rb",
    "chars": 877,
    "preview": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you"
  },
  {
    "path": "app/controllers/comments_controller.rb",
    "chars": 2138,
    "preview": "class CommentsController < ApplicationController\n  before_action :set_comment, only: [:show, :edit, :update, :destroy]\n "
  },
  {
    "path": "app/controllers/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/controllers/escalation_series_controller.rb",
    "chars": 2453,
    "preview": "class EscalationSeriesController < ApplicationController\n  before_action :set_escalation_series, only: [:show, :edit, :u"
  },
  {
    "path": "app/controllers/escalations_controller.rb",
    "chars": 2094,
    "preview": "class EscalationsController < ApplicationController\n  before_action :set_escalation, only: [:show, :edit, :update, :dest"
  },
  {
    "path": "app/controllers/home_controller.rb",
    "chars": 98,
    "preview": "class HomeController < ApplicationController\n  def index\n    redirect_to incidents_path\n  end\nend\n"
  },
  {
    "path": "app/controllers/incident_events_controller.rb",
    "chars": 1135,
    "preview": "require 'securerandom'\n\nclass IncidentEventsController < ApplicationController\n  skip_before_action :login_required, onl"
  },
  {
    "path": "app/controllers/incidents_controller.rb",
    "chars": 4838,
    "preview": "class IncidentsController < ApplicationController\n  before_action :set_incidents, only: [:index, :bulk_acknowledge, :bul"
  },
  {
    "path": "app/controllers/maintenances_controller.rb",
    "chars": 2174,
    "preview": "class MaintenancesController < ApplicationController\n  before_action :set_maintenance, only: [:show, :edit, :update, :de"
  },
  {
    "path": "app/controllers/notifier_providers_controller.rb",
    "chars": 2373,
    "preview": "class NotifierProvidersController < ApplicationController\n  before_action :set_notifier_provider, only: [:show, :edit, :"
  },
  {
    "path": "app/controllers/notifiers_controller.rb",
    "chars": 2075,
    "preview": "class NotifiersController < ApplicationController\n  before_action :set_notifier, only: [:show, :edit, :update, :destroy]"
  },
  {
    "path": "app/controllers/sessions_controller.rb",
    "chars": 357,
    "preview": "class SessionsController < ApplicationController\n  skip_before_action :login_required, only: [:create]\n\n  def create\n   "
  },
  {
    "path": "app/controllers/slack_controller.rb",
    "chars": 1368,
    "preview": "class SlackController < ApplicationController\n  skip_before_action :login_required, only: [:interactive], raise: false\n\n"
  },
  {
    "path": "app/controllers/topics_controller.rb",
    "chars": 5229,
    "preview": "class TopicsController < ApplicationController\n  before_action :set_topic, only: [:show, :edit, :update, :destroy, :mail"
  },
  {
    "path": "app/controllers/users_controller.rb",
    "chars": 2233,
    "preview": "class UsersController < ApplicationController\n  before_action :set_user, only: [:show, :edit, :update, :destroy, :activa"
  },
  {
    "path": "app/helpers/application_helper.rb",
    "chars": 29,
    "preview": "module ApplicationHelper\nend\n"
  },
  {
    "path": "app/helpers/comments_helper.rb",
    "chars": 26,
    "preview": "module CommentsHelper\nend\n"
  },
  {
    "path": "app/helpers/escalation_series_helper.rb",
    "chars": 34,
    "preview": "module EscalationSeriesHelper\nend\n"
  },
  {
    "path": "app/helpers/escalations_helper.rb",
    "chars": 29,
    "preview": "module EscalationsHelper\nend\n"
  },
  {
    "path": "app/helpers/home_helper.rb",
    "chars": 22,
    "preview": "module HomeHelper\nend\n"
  },
  {
    "path": "app/helpers/incident_events_helper.rb",
    "chars": 32,
    "preview": "module IncidentEventsHelper\nend\n"
  },
  {
    "path": "app/helpers/incidents_helper.rb",
    "chars": 27,
    "preview": "module IncidentsHelper\nend\n"
  },
  {
    "path": "app/helpers/maintenances_helper.rb",
    "chars": 30,
    "preview": "module MaintenancesHelper\nend\n"
  },
  {
    "path": "app/helpers/notifier_providers_helper.rb",
    "chars": 35,
    "preview": "module NotifierProvidersHelper\nend\n"
  },
  {
    "path": "app/helpers/notifiers_helper.rb",
    "chars": 27,
    "preview": "module NotifiersHelper\nend\n"
  },
  {
    "path": "app/helpers/sessions_helper.rb",
    "chars": 26,
    "preview": "module SessionsHelper\nend\n"
  },
  {
    "path": "app/helpers/slack_helper.rb",
    "chars": 23,
    "preview": "module SlackHelper\nend\n"
  },
  {
    "path": "app/helpers/topics_helper.rb",
    "chars": 24,
    "preview": "module TopicsHelper\nend\n"
  },
  {
    "path": "app/helpers/users_helper.rb",
    "chars": 23,
    "preview": "module UsersHelper\nend\n"
  },
  {
    "path": "app/mailers/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/application_record.rb",
    "chars": 109,
    "preview": "# Base ApplicationRecord Class\nclass ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "app/models/comment.rb",
    "chars": 190,
    "preview": "class Comment < ApplicationRecord\n  belongs_to :incident\n  belongs_to :user\n\n  validates :incident, presence: true\n  val"
  },
  {
    "path": "app/models/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/escalation.rb",
    "chars": 285,
    "preview": "class Escalation < ApplicationRecord\n  belongs_to :escalation_series\n  belongs_to :escalate_to, class_name: 'User'\n\n  va"
  },
  {
    "path": "app/models/escalation_series.rb",
    "chars": 3738,
    "preview": "class EscalationSeries < ApplicationRecord\n  has_many :escalations, dependent: :destroy\n  has_many :topics, dependent: :"
  },
  {
    "path": "app/models/escalation_update_worker.rb",
    "chars": 326,
    "preview": "class EscalationUpdateWorker\n  include Sidekiq::Worker\n\n  def perform\n    EscalationSeries.all.each do |series|\n      ha"
  },
  {
    "path": "app/models/escalation_worker.rb",
    "chars": 577,
    "preview": "class EscalationWorker\n  include Sidekiq::Worker\n\n  def self.enqueue(incident, escalation)\n    Rails.logger.info \"Enqueu"
  },
  {
    "path": "app/models/incident.rb",
    "chars": 1093,
    "preview": "require 'digest/sha1'\n\nclass Incident < ApplicationRecord\n  STATUSES = [:opened, :acknowledged, :resolved]\n\n  belongs_to"
  },
  {
    "path": "app/models/incident_event.rb",
    "chars": 933,
    "preview": "class IncidentEvent < ApplicationRecord\n  belongs_to :incident\n\n  enum kind: [:opened, :acknowledged, :resolved, :escala"
  },
  {
    "path": "app/models/maintenance.rb",
    "chars": 323,
    "preview": "class Maintenance < ApplicationRecord\n  scope :active, -> { t = Time.now; where('start_time <= ? AND ? <= end_time', t, "
  },
  {
    "path": "app/models/notification_worker.rb",
    "chars": 427,
    "preview": "class NotificationWorker\n  include Sidekiq::Worker\n\n  def self.enqueue(event:, notifier:)\n    Rails.logger.info \"Enqueue"
  },
  {
    "path": "app/models/notifier.rb",
    "chars": 537,
    "preview": "class Notifier < ApplicationRecord\n  belongs_to :provider, class_name: 'NotifierProvider'\n  belongs_to :user\n  belongs_t"
  },
  {
    "path": "app/models/notifier_provider.rb",
    "chars": 11791,
    "preview": "class NotifierProvider < ApplicationRecord\n  serialize :settings, JSON\n  enum kind: [:mailgun, :file, :rails_logger, :hi"
  },
  {
    "path": "app/models/topic.rb",
    "chars": 501,
    "preview": "class Topic < ApplicationRecord\n  enum kind: [:api]\n  belongs_to :escalation_series\n  has_many :incidents\n\n  validates :"
  },
  {
    "path": "app/models/user.rb",
    "chars": 893,
    "preview": "require 'securerandom'\n\nclass User < ApplicationRecord\n  scope :active, -> { where(active: true) }\n\n  has_many :notifier"
  },
  {
    "path": "app/views/comments/_form.html.erb",
    "chars": 796,
    "preview": "<%= form_for([@incident, @comment]) do |f| %>\n  <% if @comment.errors.any? %>\n    <div class=\"alert alert-danger\">\n     "
  },
  {
    "path": "app/views/comments/edit.html.erb",
    "chars": 141,
    "preview": "<h1>Editing Comment</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', [@incident, @comment] %> |\n<%= link_to 'Back', incid"
  },
  {
    "path": "app/views/comments/index.html.erb",
    "chars": 758,
    "preview": "<h1>Listing Comments</h1>\n\n<p>\n  <strong>Incident:</strong>\n  <pre><%= @incident.subject %></pre>\n</p>\n\n<table class=\"ta"
  },
  {
    "path": "app/views/comments/index.json.jbuilder",
    "chars": 116,
    "preview": "json.array!(@comments) do |comment|\n  json.extract! comment, :id\n  json.url comment_url(comment, format: :json)\nend\n"
  },
  {
    "path": "app/views/comments/new.html.erb",
    "chars": 90,
    "preview": "<h1>New Comment</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', incident_comments_path %>\n"
  },
  {
    "path": "app/views/comments/show.html.erb",
    "chars": 398,
    "preview": "\n<p>\n  <strong>Incident:</strong>\n  <pre><%= @incident.subject %></pre>\n</p>\n\n<p>\n  <strong>Comment:</strong>\n  <pre sty"
  },
  {
    "path": "app/views/comments/show.json.jbuilder",
    "chars": 61,
    "preview": "json.extract! @comment, @user, :id, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/escalation_series/_form.html.erb",
    "chars": 772,
    "preview": "<%= form_for(@escalation_series) do |f| %>\n  <% if @escalation_series.errors.any? %>\n    <div class=\"alert alert-danger\""
  },
  {
    "path": "app/views/escalation_series/edit.html.erb",
    "chars": 154,
    "preview": "<h1>Editing Escalation Series</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @escalation_series %> |\n<%= link_to 'Back'"
  },
  {
    "path": "app/views/escalation_series/index.html.erb",
    "chars": 652,
    "preview": "\n<h1>Listing Escalation Series</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th colspan=\"3\">"
  },
  {
    "path": "app/views/escalation_series/index.json.jbuilder",
    "chars": 172,
    "preview": "json.array!(@escalation_series) do |escalation_series|\n  json.extract! escalation_series, :id, :name\n  json.url escalati"
  },
  {
    "path": "app/views/escalation_series/new.html.erb",
    "chars": 106,
    "preview": "<h1>New Escalation Series</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', escalation_series_index_path %>\n"
  },
  {
    "path": "app/views/escalation_series/show.html.erb",
    "chars": 477,
    "preview": "\n<p>\n  <strong>Name:</strong>\n  <%= @escalation_series.name %>\n</p>\n\n<table class=\"table\">\n  <% @escalation_series.escal"
  },
  {
    "path": "app/views/escalation_series/show.json.jbuilder",
    "chars": 71,
    "preview": "json.extract! @escalation_series, :id, :name, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/escalations/_form.html.erb",
    "chars": 945,
    "preview": "<%= form_for(@escalation) do |f| %>\n  <% if @escalation.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2><%"
  },
  {
    "path": "app/views/escalations/edit.html.erb",
    "chars": 128,
    "preview": "<h1>Editing Escalation</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @escalation %> |\n<%= link_to 'Back', escalations_"
  },
  {
    "path": "app/views/escalations/index.html.erb",
    "chars": 782,
    "preview": "\n<h1>Listing Escalations</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Escalation Series</th>\n      <th>Escal"
  },
  {
    "path": "app/views/escalations/index.json.jbuilder",
    "chars": 192,
    "preview": "json.array!(@escalations) do |escalation|\n  json.extract! escalation, :id, :escalate_to_id, :escalate_after_sec, :escala"
  },
  {
    "path": "app/views/escalations/new.html.erb",
    "chars": 87,
    "preview": "<h1>New Escalation</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', escalations_path %>\n"
  },
  {
    "path": "app/views/escalations/show.html.erb",
    "chars": 361,
    "preview": "\n<p>\n  <strong>Escalation series:</strong>\n  <%= @escalation.escalation_series.name %>\n</p>\n\n<p>\n  <strong>Escalate to:<"
  },
  {
    "path": "app/views/escalations/show.json.jbuilder",
    "chars": 95,
    "preview": "json.extract! @escalation, :id, :escalate_to_id, :escalate_after_sec, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/home/index.html.erb",
    "chars": 68,
    "preview": "<h1>Home#index</h1>\n<p>Find me in app/views/home/index.html.erb</p>\n"
  },
  {
    "path": "app/views/incident_events/twilio.html.erb",
    "chars": 91,
    "preview": "<h1>IncidentEvents#twilio</h1>\n<p>Find me in app/views/incident_events/twilio.html.erb</p>\n"
  },
  {
    "path": "app/views/incidents/_form.html.erb",
    "chars": 967,
    "preview": "<%= form_for(@incident) do |f| %>\n  <% if @incident.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2><%= pl"
  },
  {
    "path": "app/views/incidents/edit.html.erb",
    "chars": 122,
    "preview": "<h1>Editing Incident</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @incident %> |\n<%= link_to 'Back', incidents_path %"
  },
  {
    "path": "app/views/incidents/index.html.erb",
    "chars": 3489,
    "preview": "\n<h1>Listing Incidents</h1>\n\n<div class=\"btn-toolbar\" role=\"toolbar\" style=\"margin-bottom: 15px\">\n  <div class=\"btn-grou"
  },
  {
    "path": "app/views/incidents/index.json.jbuilder",
    "chars": 169,
    "preview": "json.array!(@incidents) do |incident|\n  json.extract! incident, :id, :subject, :description, :topic_id, :occured_at\n  js"
  },
  {
    "path": "app/views/incidents/new.html.erb",
    "chars": 83,
    "preview": "<h1>New Incident</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', incidents_path %>\n"
  },
  {
    "path": "app/views/incidents/show.html.erb",
    "chars": 1279,
    "preview": "\n<p>\n  <strong>Subject:</strong>\n  <%= @incident.subject %>\n</p>\n\n<p>\n  <strong>Description:</strong>\n  <pre><%= @incide"
  },
  {
    "path": "app/views/incidents/show.json.jbuilder",
    "chars": 103,
    "preview": "json.extract! @incident, :id, :subject, :description, :topic_id, :occured_at, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/layouts/application.html.erb",
    "chars": 2175,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Waker</title>\n  <%= stylesheet_link_tag    'application', media: 'all' %>\n  <%= j"
  },
  {
    "path": "app/views/maintenances/_form.html.erb",
    "chars": 1011,
    "preview": "<%= form_for(@maintenance) do |f| %>\n  <% if @maintenance.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2>"
  },
  {
    "path": "app/views/maintenances/edit.html.erb",
    "chars": 131,
    "preview": "<h1>Editing Maintenance</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @maintenance %> |\n<%= link_to 'Back', maintenanc"
  },
  {
    "path": "app/views/maintenances/index.html.erb",
    "chars": 871,
    "preview": "\n<h1>Listing Maintenances</h1>\n\n<p>Expired maintanances are not shown</p>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n    "
  },
  {
    "path": "app/views/maintenances/index.json.jbuilder",
    "chars": 171,
    "preview": "json.array!(@maintenances) do |maintenance|\n  json.extract! maintenance, :id, :topic_id, :start_time, :end_time\n  json.u"
  },
  {
    "path": "app/views/maintenances/new.html.erb",
    "chars": 89,
    "preview": "<h1>New Maintenance</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', maintenances_path %>\n"
  },
  {
    "path": "app/views/maintenances/show.html.erb",
    "chars": 437,
    "preview": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <strong>Topic:</strong>\n  <%= @maintenance.topic.name %>\n</p>\n\n<p>\n  <strong>Fil"
  },
  {
    "path": "app/views/maintenances/show.json.jbuilder",
    "chars": 93,
    "preview": "json.extract! @maintenance, :id, :topic_id, :start_time, :end_time, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/notifier_providers/_form.html.erb",
    "chars": 1070,
    "preview": "<%= form_for(@notifier_provider) do |f| %>\n  <% if @notifier_provider.errors.any? %>\n    <div class=\"alert alert-danger\""
  },
  {
    "path": "app/views/notifier_providers/edit.html.erb",
    "chars": 149,
    "preview": "<h1>Editing Notifier Provider</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @notifier_provider %> |\n<%= link_to 'Back'"
  },
  {
    "path": "app/views/notifier_providers/hipchat/acknowledged.text.erb",
    "chars": 198,
    "preview": "Incident acknowledged: <%= event.incident.subject %> (<a href=\"<%= Rails.application.routes.url_helpers.resolve_incident"
  },
  {
    "path": "app/views/notifier_providers/hipchat/escalated.text.erb",
    "chars": 382,
    "preview": "Incident escalated to <%= event.escalated_to.name %>: <%= event.incident.subject %> (<a href=\"<%= Rails.application.rout"
  },
  {
    "path": "app/views/notifier_providers/hipchat/opened.text.erb",
    "chars": 349,
    "preview": "New incident opened: <%= event.incident.subject %> (<a href=\"<%= Rails.application.routes.url_helpers.acknowledge_incide"
  },
  {
    "path": "app/views/notifier_providers/hipchat/resolved.text.erb",
    "chars": 49,
    "preview": "Incident resolved: <%= event.incident.subject %>\n"
  },
  {
    "path": "app/views/notifier_providers/index.html.erb",
    "chars": 796,
    "preview": "\n<h1>Listing Notifier Providers</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th>Kind</th>\n "
  },
  {
    "path": "app/views/notifier_providers/index.json.jbuilder",
    "chars": 191,
    "preview": "json.array!(@notifier_providers) do |notifier_provider|\n  json.extract! notifier_provider, :id, :name, :kind, :settings\n"
  },
  {
    "path": "app/views/notifier_providers/mailgun/default.html.erb",
    "chars": 1940,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initia"
  },
  {
    "path": "app/views/notifier_providers/mailgun/default.text.erb",
    "chars": 357,
    "preview": "<%= event.incident.subject %>\n====\n\nActions:\n\n- To ack: <%= Rails.application.routes.url_helpers.acknowledge_incident_ur"
  },
  {
    "path": "app/views/notifier_providers/new.html.erb",
    "chars": 101,
    "preview": "<h1>New Notifier Provider</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', notifier_providers_path %>\n"
  },
  {
    "path": "app/views/notifier_providers/rails_logger/default.text.erb",
    "chars": 32,
    "preview": "Notification: <%= event.kind %>\n"
  },
  {
    "path": "app/views/notifier_providers/show.html.erb",
    "chars": 333,
    "preview": "\n<p>\n  <strong>Name:</strong>\n  <%= @notifier_provider.name %>\n</p>\n\n<p>\n  <strong>Kind:</strong>\n  <%= @notifier_provid"
  },
  {
    "path": "app/views/notifier_providers/show.json.jbuilder",
    "chars": 89,
    "preview": "json.extract! @notifier_provider, :id, :name, :kind, :settings, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/notifiers/_form.html.erb",
    "chars": 1392,
    "preview": "<%= form_for(@notifier) do |f| %>\n  <% if @notifier.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2><%= pl"
  },
  {
    "path": "app/views/notifiers/edit.html.erb",
    "chars": 122,
    "preview": "<h1>Editing Notifier</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @notifier %> |\n<%= link_to 'Back', notifiers_path %"
  },
  {
    "path": "app/views/notifiers/index.html.erb",
    "chars": 952,
    "preview": "\n<h1>Listing Notifiers</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>User</th>\n      <th>Topic</th>\n      <th"
  },
  {
    "path": "app/views/notifiers/index.json.jbuilder",
    "chars": 223,
    "preview": "json.array!(@notifiers) do |notifier|\n  json.extract! notifier, :id, :settings, :provider_id, :provider, :topic_id, :use"
  },
  {
    "path": "app/views/notifiers/new.html.erb",
    "chars": 83,
    "preview": "<h1>New Notifier</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', notifiers_path %>\n"
  },
  {
    "path": "app/views/notifiers/show.html.erb",
    "chars": 548,
    "preview": "\n<p>\n  <strong>Provider:</strong>\n  <%= @notifier.provider.name %>\n</p>\n\n<p>\n  <strong>User:</strong>\n  <%= @notifier.us"
  },
  {
    "path": "app/views/notifiers/show.json.jbuilder",
    "chars": 131,
    "preview": "json.extract! @notifier, :id, :settings, :provider_id, :provider, :topic_id, :user_id, :notify_after_sec, :created_at, :"
  },
  {
    "path": "app/views/sessions/create.html.erb",
    "chars": 78,
    "preview": "<h1>Sessions#create</h1>\n<p>Find me in app/views/sessions/create.html.erb</p>\n"
  },
  {
    "path": "app/views/slack/interactive.html.erb",
    "chars": 82,
    "preview": "<h1>Slack#interactive</h1>\n<p>Find me in app/views/slack/interactive.html.erb</p>\n"
  },
  {
    "path": "app/views/topics/_form.html.erb",
    "chars": 1156,
    "preview": "<%= form_for(@topic) do |f| %>\n  <% if @topic.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2><%= pluraliz"
  },
  {
    "path": "app/views/topics/edit.html.erb",
    "chars": 113,
    "preview": "<h1>Editing Topic</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @topic %> |\n<%= link_to 'Back', topics_path %>\n"
  },
  {
    "path": "app/views/topics/index.html.erb",
    "chars": 751,
    "preview": "\n<h1>Listing Topics</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th>Kind</th>\n      <th>Esc"
  },
  {
    "path": "app/views/topics/index.json.jbuilder",
    "chars": 120,
    "preview": "json.array!(@topics) do |topic|\n  json.extract! topic, :id, :name, :type\n  json.url topic_url(topic, format: :json)\nend\n"
  },
  {
    "path": "app/views/topics/new.html.erb",
    "chars": 77,
    "preview": "<h1>New Topic</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', topics_path %>\n"
  },
  {
    "path": "app/views/topics/show.html.erb",
    "chars": 799,
    "preview": "\n<p>\n  <strong>Name:</strong>\n  <%= @topic.name %>\n</p>\n\n<p>\n  <strong>Kind:</strong>\n  <%= @topic.kind %>\n</p>\n\n<p>\n  <"
  },
  {
    "path": "app/views/topics/show.json.jbuilder",
    "chars": 66,
    "preview": "json.extract! @topic, :id, :name, :type, :created_at, :updated_at\n"
  },
  {
    "path": "app/views/users/_form.html.erb",
    "chars": 533,
    "preview": "<%= form_for(@user) do |f| %>\n  <% if @user.errors.any? %>\n    <div class=\"alert alert-danger\">\n      <h2><%= pluralize("
  },
  {
    "path": "app/views/users/edit.html.erb",
    "chars": 110,
    "preview": "<h1>Editing User</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "app/views/users/index.html.erb",
    "chars": 940,
    "preview": "\n<h1>Listing Users</h1>\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th>Status</th>\n      <th co"
  },
  {
    "path": "app/views/users/index.json.jbuilder",
    "chars": 108,
    "preview": "json.array!(@users) do |user|\n  json.extract! user, :id, :name\n  json.url user_url(user, format: :json)\nend\n"
  },
  {
    "path": "app/views/users/new.html.erb",
    "chars": 75,
    "preview": "<h1>New User</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "app/views/users/show.html.erb",
    "chars": 206,
    "preview": "\n<p>\n  <strong>Name:</strong>\n  <%= @user.name %>\n</p>\n\n<p>\n  <strong>Provider:</strong>\n  <%= @user.provider %>\n</p>\n\n<"
  },
  {
    "path": "app/views/users/show.json.jbuilder",
    "chars": 58,
    "preview": "json.extract! @user, :id, :name, :created_at, :updated_at\n"
  },
  {
    "path": "bin/bundle",
    "chars": 129,
    "preview": "#!/usr/bin/env ruby\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\nload Gem.bin_path('bundler', '"
  },
  {
    "path": "bin/rails",
    "chars": 219,
    "preview": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path(\"../spring\", __FILE__)\nrescue LoadError\nend\nAPP_PATH = File.expand_pat"
  },
  {
    "path": "bin/rake",
    "chars": 164,
    "preview": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path(\"../spring\", __FILE__)\nrescue LoadError\nend\nrequire_relative '../confi"
  },
  {
    "path": "bin/setup",
    "chars": 805,
    "preview": "#!/usr/bin/env ruby\nrequire 'pathname'\n\n# path to your application root.\nAPP_ROOT = Pathname.new File.expand_path('../.."
  },
  {
    "path": "bin/spring",
    "chars": 517,
    "preview": "#!/usr/bin/env ruby\n\n# This file loads spring without using Bundler, in order to be fast\n# It gets overwritten when you "
  },
  {
    "path": "config/application.rb",
    "chars": 1816,
    "preview": "require File.expand_path('../boot', __FILE__)\n\n# Pick the frameworks you want:\nrequire \"active_model/railtie\"\nrequire \"a"
  },
  {
    "path": "config/boot.rb",
    "chars": 132,
    "preview": "ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' # Set up gems listed in t"
  },
  {
    "path": "config/database.yml",
    "chars": 629,
    "preview": "production:\n  adapter: mysql2\n  database: waker\n  encoding: utf8mb4\n  username : <%= ENV['MYSQL_USER'] || 'root' %>\n  pa"
  },
  {
    "path": "config/environment.rb",
    "chars": 150,
    "preview": "# Load the Rails application.\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the Rails application.\n"
  },
  {
    "path": "config/environments/development.rb",
    "chars": 1673,
    "preview": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  "
  },
  {
    "path": "config/environments/production.rb",
    "chars": 3629,
    "preview": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  "
  },
  {
    "path": "config/environments/test.rb",
    "chars": 1755,
    "preview": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  "
  },
  {
    "path": "config/initializers/assets.rb",
    "chars": 486,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire"
  },
  {
    "path": "config/initializers/backtrace_silencers.rb",
    "chars": 404,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're"
  },
  {
    "path": "config/initializers/cookies_serializer.rb",
    "chars": 129,
    "preview": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.action_dispatch.cookies_serializer"
  },
  {
    "path": "config/initializers/field_with_errors.rb",
    "chars": 171,
    "preview": "Rails.application.configure do\n  config.action_view.field_error_proc = lambda do |html_tag, instance|\n    %Q{<div class="
  },
  {
    "path": "config/initializers/filter_parameter_logging.rb",
    "chars": 194,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered fro"
  },
  {
    "path": "config/initializers/inflections.rb",
    "chars": 647,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Infl"
  },
  {
    "path": "config/initializers/mime_types.rb",
    "chars": 156,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::"
  },
  {
    "path": "config/initializers/omniauth.rb",
    "chars": 38,
    "preview": "OmniAuth.config.logger = Rails.logger\n"
  },
  {
    "path": "config/initializers/session_store.rb",
    "chars": 137,
    "preview": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :cookie_store, key: "
  },
  {
    "path": "config/initializers/sidekiq.rb",
    "chars": 427,
    "preview": "namespace = ENV['REDIS_NAMESPACE'] || \"waker-#{Rails.env}\"\nhost = ENV['REDIS_HOST'] || 'localhost'\nport = ENV['REDIS_POR"
  },
  {
    "path": "config/initializers/url_options.rb",
    "chars": 239,
    "preview": "if default_host = ENV['DEFAULT_HOST']\n  Rails.application.routes.default_url_options[:host] = default_host\nend\n\nif defau"
  },
  {
    "path": "config/initializers/wrap_parameters.rb",
    "chars": 517,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsW"
  },
  {
    "path": "config/locales/en.yml",
    "chars": 634,
    "preview": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If yo"
  },
  {
    "path": "config/routes.rb",
    "chars": 2641,
    "preview": "Rails.application.routes.draw do\n  post 'slack/interactive'\n\n  get '/auth/:provider/callback', to: 'sessions#create'\n\n  "
  },
  {
    "path": "config/secrets.yml",
    "chars": 964,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of si"
  },
  {
    "path": "config.ru",
    "chars": 153,
    "preview": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',"
  },
  {
    "path": "db/migrate/20150120134616_create_topics.rb",
    "chars": 183,
    "preview": "class CreateTopics < ActiveRecord::Migration\n  def change\n    create_table :topics do |t|\n      t.string :name\n      t.i"
  },
  {
    "path": "db/migrate/20150120134747_create_users.rb",
    "chars": 159,
    "preview": "class CreateUsers < ActiveRecord::Migration\n  def change\n    create_table :users do |t|\n      t.string :name\n\n      t.ti"
  },
  {
    "path": "db/migrate/20150120134905_create_notifiers.rb",
    "chars": 225,
    "preview": "class CreateNotifiers < ActiveRecord::Migration\n  def change\n    create_table :notifiers do |t|\n      t.integer :type\n  "
  },
  {
    "path": "db/migrate/20150120135017_create_shifts.rb",
    "chars": 214,
    "preview": "class CreateShifts < ActiveRecord::Migration\n  def change\n    create_table :shifts do |t|\n      t.references :user, inde"
  },
  {
    "path": "db/migrate/20150120135123_create_escalations.rb",
    "chars": 298,
    "preview": "class CreateEscalations < ActiveRecord::Migration\n  def change\n    create_table :escalations do |t|\n      t.references :"
  },
  {
    "path": "db/migrate/20150120135244_create_escalation_series.rb",
    "chars": 182,
    "preview": "class CreateEscalationSeries < ActiveRecord::Migration\n  def change\n    create_table :escalation_series do |t|\n      t.s"
  },
  {
    "path": "db/migrate/20150120135351_add_escalation_series_to_escalation.rb",
    "chars": 204,
    "preview": "class AddEscalationSeriesToEscalation < ActiveRecord::Migration\n  def change\n    add_reference :escalations, :escalation"
  },
  {
    "path": "db/migrate/20150120141627_rename_type_with_kind_of_topic.rb",
    "chars": 121,
    "preview": "class RenameTypeWithKindOfTopic < ActiveRecord::Migration\n  def change\n    rename_column :topics, :type, :kind\n  end\nend"
  },
  {
    "path": "db/migrate/20150120142452_create_incidents.rb",
    "chars": 304,
    "preview": "class CreateIncidents < ActiveRecord::Migration\n  def change\n    create_table :incidents do |t|\n      t.string :subject\n"
  }
]

// ... and 70 more files (download for full content)

About this extraction

This page contains the full source code of the ryotarai/waker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 270 files (156.8 KB), approximately 50.7k tokens, and a symbol index with 338 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!