Skip to content

Commit

Permalink
Adds Project with a Project Channel
Browse files Browse the repository at this point in the history
For client projects, we will likely have a project-specific Slack channel. We would like to have Beggar notificy the project-specific Slack channel of pull requests in addition to any channels that may be tagged in the PR description.

A Project will require a Channel. Channels can be tied to multiple projects. There's no firm requirement these channels be project specific, but that's how I see this being used.

To add a Project Channel and a Project, use Beggar's Active Admin GUI.
  • Loading branch information
Nathan L. Walls committed Apr 3, 2015
1 parent d28fc86 commit ce3c82a
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 12 deletions.
30 changes: 28 additions & 2 deletions app/actions/create_new_pr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ def initialize(payload_parser, *)

def self.matches(payload_parser, *)
payload_parser.action == "opened" &&
TagParser.new.parse(payload_parser.body).any?
(
tags_matched(payload_parser.body) ||
project_matched(payload_parser.repo_github_url)
)
end

def call
Expand All @@ -20,14 +23,37 @@ def call

private

def self.project_matched(repo_github_url)
ProjectMatcher.match(repo_github_url).any?
end

def self.tags_matched(body)
TagParser.new.parse(body).any?
end

def channels
[tag_channels, project_channels].flatten.compact
end

def project_channels
@projects ||= begin
projects = ProjectMatcher.match(payload_parser.repo_github_url)
unique_channels(list: projects, method: :default_channel)
end
end

def tag_channels
@tags ||= begin
tag_names = TagParser.new.parse(payload_parser.body)
tag_names.map(&Channel.method(:with_tag_name)).compact.uniq
unique_channels(list: tag_names, method: Channel.method(:with_tag_name))
end
end

def post_to_slack(pull_request)
WebhookNotifier.new(pull_request).send_notification
end

def unique_channels(list:, method:)
list.map(&method).compact.uniq
end
end
3 changes: 3 additions & 0 deletions app/admin/project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ActiveAdmin.register Project do
permit_params :name, :default_channel_id, :github_url
end
1 change: 1 addition & 0 deletions app/models/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class Channel < ActiveRecord::Base
validates :name, uniqueness: { scope: :webhook_url }
validates :webhook_url, presence: true

has_many :projects, as: :default_channel, dependent: :destroy
has_many :tags, dependent: :destroy
has_and_belongs_to_many :active_pull_requests, -> { active }, class_name: "PullRequest"

Expand Down
7 changes: 7 additions & 0 deletions app/models/project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Project < ActiveRecord::Base
validates :default_channel, presence: true
validates :github_url, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: true

belongs_to :default_channel, class_name: "Channel"
end
8 changes: 4 additions & 4 deletions app/services/payload_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def params
}
end

def repo_github_url
pull_request["head"]["repo"]["html_url"]
end

protected

attr_reader :headers, :payload
Expand All @@ -61,10 +65,6 @@ def repo_name
pull_request["head"]["repo"]["full_name"]
end

def repo_github_url
pull_request["head"]["repo"]["html_url"]
end

def title
pull_request["title"]
end
Expand Down
5 changes: 5 additions & 0 deletions app/services/project_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ProjectMatcher
def self.match(github_url)
Project.where(github_url: github_url)
end
end
16 changes: 16 additions & 0 deletions db/migrate/20150313144216_create_project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class CreateProject < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :name, null: false, unique: true
t.string :github_url, null: false, unique: true
t.integer :default_channel_id, null: false
end

add_foreign_key(
:projects,
:channels,
column: :default_channel_id,
on_delete: :cascade
)
end
end
19 changes: 13 additions & 6 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20150304195244) do
ActiveRecord::Schema.define(version: 20150313144216) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

create_table "channels", force: true do |t|
create_table "channels", force: :cascade do |t|
t.string "name", null: false
t.string "webhook_url", null: false
end

add_index "channels", ["name"], name: "index_channels_on_name", using: :btree

create_table "channels_pull_requests", force: true do |t|
create_table "channels_pull_requests", force: :cascade do |t|
t.integer "channel_id"
t.integer "pull_request_id"
end

add_index "channels_pull_requests", ["channel_id", "pull_request_id"], name: "index_channels_pull_requests_on_channel_id_and_pull_request_id", unique: true, using: :btree

create_table "delayed_jobs", force: true do |t|
create_table "delayed_jobs", force: :cascade do |t|
t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false
t.text "handler", null: false
Expand All @@ -46,7 +46,13 @@

add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree

create_table "pull_requests", force: true do |t|
create_table "projects", force: :cascade do |t|
t.string "name", null: false
t.string "github_url", null: false
t.integer "default_channel_id", null: false
end

create_table "pull_requests", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "github_url", null: false
Expand All @@ -64,11 +70,12 @@

add_index "pull_requests", ["status"], name: "index_pull_requests_on_status", using: :btree

create_table "tags", force: true do |t|
create_table "tags", force: :cascade do |t|
t.string "name", null: false
t.integer "channel_id", null: false
end

add_index "tags", ["name"], name: "index_tags_on_name", using: :btree

add_foreign_key "projects", "channels", column: "default_channel_id", on_delete: :cascade
end
90 changes: 90 additions & 0 deletions spec/actions/create_new_pr_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,100 @@
:parser,
action: "opened",
body: "There are no tags here",
repo_github_url: "http://example.com/"
)

expect(CreateNewPr.matches(parser, nil)).to be false
end
end

context "when there is a project" do
it "is true with tags" do
project_url = "http://example.com/thoughtbot/beggar"
create(:project, github_url: project_url)
parser = double(
:parser,
action: "opened",
body: "#rails",
repo_github_url: project_url
)

expect(CreateNewPr.matches(parser, nil)).to be true
end

it "is true without tags" do
project_url = "https://github.com/thoughtbot/beggar"
create(:project, github_url: project_url)
parser = double(
:parser,
action: "opened",
body: "No tags, no tags",
repo_github_url: project_url
)

expect(CreateNewPr.matches(parser, nil)).to be true
end
end
end

describe ".channels" do
context "when there is a project" do
it "includes the project default channel and any tags" do
rails_channel = create(:channel, tag_name: "rails")
design_channel = create(:channel, tag_name: "design")
project_channel = create(:channel, name: "project")

project_url = "https://github.com/thoughtbot/beggar"
create(
:project,
github_url: project_url,
default_channel: project_channel
)

channels_list = {
channels: [
rails_channel,
design_channel,
project_channel
]
}

parser = double(
:parser,
action: "opened",
body: "#rails #design",
params: spy(Hash.new),
repo_github_url: project_url
)

pr_creator = CreateNewPr.new(parser, nil)
allow(pr_creator).to receive(:post_to_slack)

pr_creator.call

expect(parser.params).to have_received(:merge).with(channels_list)
end
end

context "when there isn't a project" do
it "includes channels based on tags" do
rails_channel = create(:channel, tag_name: "rails")
design_channel = create(:channel, tag_name: "design")
channels_hash = { channels: [rails_channel, design_channel] }
parser = double(
:parser,
action: "opened",
body: "#rails #design",
params: spy(Hash.new),
repo_github_url: "_"
)
pr_creator = CreateNewPr.new(parser, nil)
allow(pr_creator).to receive(:post_to_slack)

pr_creator.call

expect(parser.params).to have_received(:merge).with(channels_hash)
end
end
end
end
6 changes: 6 additions & 0 deletions spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
end
end

factory :project do
sequence(:name) { |n| "project#{n}" }
sequence(:github_url) { |n| "http://example.com/thoughtbot/project#{n}" }
association :default_channel, factory: :channel
end

factory :pull_request do
sequence(:github_url) {|n| "https://github.com/thoughtbot/stuff/pulls/#{n}"}
repo_github_url { "https://github.com/#{repo_name}" }
Expand Down
1 change: 1 addition & 0 deletions spec/models/channel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
it { should validate_presence_of(:name) }
it { should validate_presence_of(:webhook_url) }

it { should have_many(:projects).dependent(:destroy) }
it { should have_many(:tags).dependent(:destroy) }
it { should have_and_belong_to_many(:active_pull_requests) }

Expand Down
11 changes: 11 additions & 0 deletions spec/models/project_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "rails_helper"

describe Project do
subject { FactoryGirl.build(:project) }
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name) }
it { should belong_to(:default_channel) }
it { should validate_presence_of(:default_channel) }
it { should validate_presence_of(:github_url) }
it { should validate_uniqueness_of(:github_url) }
end
13 changes: 13 additions & 0 deletions spec/services/project_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "spec_helper"
require "services/project_matcher"

describe ProjectMatcher do
it "matches when a project has the same github repo name as provided text" do
url = "http://example.com/thoughtbot/beggar"
create(:project, github_url: url)

result = ProjectMatcher.match(url)

expect(result.first.github_url).to eq(url)
end
end

0 comments on commit ce3c82a

Please sign in to comment.