From 6a9eed19f8e27ecb1c6bf06d6bbc7da8936f9fa7 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 1 Dec 2018 13:16:58 -0500 Subject: [PATCH 01/11] Added null_github_org_hook.rb and tests --- app/models/null_github_org_hook.rb | 23 +++++++++++++ spec/models/null_github_org_hook_spec.rb | 43 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 app/models/null_github_org_hook.rb create mode 100644 spec/models/null_github_org_hook_spec.rb diff --git a/app/models/null_github_org_hook.rb b/app/models/null_github_org_hook.rb new file mode 100644 index 0000000000..ccce7176aa --- /dev/null +++ b/app/models/null_github_org_hook.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class NullGitHubOrgHook < NullGitHubResource + def active + false + end + + def active? + active + end + + def name + nil + end + + def created_at + nil + end + + def updated_at + nil + end +end diff --git a/spec/models/null_github_org_hook_spec.rb b/spec/models/null_github_org_hook_spec.rb new file mode 100644 index 0000000000..d454440529 --- /dev/null +++ b/spec/models/null_github_org_hook_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe NullGitHubOrgHook do + subject { described_class.new } + + describe "#active" do + it "returns false" do + expect(subject.active).to be_falsy + end + end + + describe "#active?" do + it "returns false" do + expect(subject.active?).to be_falsy + end + end + + describe "#name" do + it "returns nil" do + expect(subject.name).to be_nil + end + end + + describe "#created_at" do + it "returns nil" do + expect(subject.created_at).to be_nil + end + end + + describe "#updated_at" do + it "returns nil" do + expect(subject.updated_at).to be_nil + end + end + + describe "#null?" do + it "returns true" do + expect(subject.null?).to be(true) + end + end +end From 6c760563ce05fb4b4d918b126492fe562a945bd6 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 1 Dec 2018 13:20:19 -0500 Subject: [PATCH 02/11] Added active? convenience method --- app/models/github_org_hook.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/github_org_hook.rb b/app/models/github_org_hook.rb index 4dda858dbd..38c2fc0eff 100644 --- a/app/models/github_org_hook.rb +++ b/app/models/github_org_hook.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class GitHubOrgHook < GitHubOrgResource + def active? + active + end + private def github_attributes From d69465e256bbda53b92bd9d087ed6cf036c303e3 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 1 Dec 2018 13:21:21 -0500 Subject: [PATCH 03/11] Added ensure_webhook_is_active! method --- app/models/organization_webhook.rb | 40 +++++++- spec/models/organization_webhook_spec.rb | 120 +++++++++++++++++++++-- 2 files changed, 150 insertions(+), 10 deletions(-) diff --git a/app/models/organization_webhook.rb b/app/models/organization_webhook.rb index 3ef4e5de79..395a93cdea 100644 --- a/app/models/organization_webhook.rb +++ b/app/models/organization_webhook.rb @@ -2,6 +2,10 @@ class OrganizationWebhook < ApplicationRecord class NoValidTokenError < StandardError; end + WEBHOOK_URL_PRODUCTION_ERROR = "WebHook failed to be created,"\ + " please open an issue at https://github.com/education/classroom/issues/new" + WEBHOOK_URL_DEVELOPMENT_ERROR = "CLASSROOM_WEBHOOK_URL_PREFIX is not set,"\ + " please check your .env file." has_many :organizations has_many :users, through: :organizations @@ -11,6 +15,15 @@ class NoValidTokenError < StandardError; end validates :github_organization_id, presence: true validates :github_organization_id, uniqueness: true + def github_org_hook(client) + @github_org_hook ||= GitHubOrgHook.new( + client, + github_organization_id, + github_id, + headers: GitHub::APIHeaders.no_cache_no_store + ) + end + # External: Finds a User's token that has the `admin:org_hook` scope # for creating the organization webhook. # @@ -35,11 +48,11 @@ def admin_org_hook_scoped_github_client # External: Creates an organization webhook, and saves it's ID. # - # client - the client that used to create the organization webhook + # client - The client that used to create the organization webhook # (Note: client must have the `admin:org_hook` scope). # # Returns true if successful, otherwise raises a GitHub::Error or ActiveRecord::RecordInvalid. - def create_org_hook!(client:) + def create_org_hook!(client) github_organization = GitHubOrganization.new(client, github_organization_id) github_id = github_organization.create_organization_webhook(config: { url: webhook_url }).id save! @@ -48,6 +61,25 @@ def create_org_hook!(client:) raise err end + # External: Checks if an org hook exists and is active, + # otherwise creates one and saves. + # + # client - The client that used to create the organization webhook + # (Note: client must have the `admin:org_hook` scope). + # If not provided, searches for Users with `admin:org_hook` scoped token. + # + # Returns true if successful. Raises a GitHub::Error or ActiveRecord::RecordInvalid if + # something goes wrong creating the org hook. Raises a NoValidTokenError if no client was passed + # and no User token with the `admin:org_hook` scope could be found. + # + # Warning: If no client argument is passed, this could potentially take very long for organizations + # of a large size. Invoke cautiously. + def ensure_webhook_is_active!(client: nil) + client ||= admin_org_hook_scoped_github_client + create_org_hook!(client) unless github_id.present? && github_org_hook(client).active? + true + end + private # Internal: Find Users that has the `admin:org_hook` scope @@ -98,9 +130,9 @@ def webhook_url webhook_url_prefix = ENV["CLASSROOM_WEBHOOK_URL_PREFIX"] error_message = if Rails.env.production? - "WebHook failed to be created, please open an issue at https://github.com/education/classroom/issues/new" # rubocop:disable Metrics/LineLength + WEBHOOK_URL_PRODUCTION_ERROR else - "CLASSROOM_WEBHOOK_URL_PREFIX is not set, please check your .env file" + WEBHOOK_URL_DEVELOPMENT_ERROR end raise error_message if webhook_url_prefix.blank? diff --git a/spec/models/organization_webhook_spec.rb b/spec/models/organization_webhook_spec.rb index 19301c2d03..131898eccd 100644 --- a/spec/models/organization_webhook_spec.rb +++ b/spec/models/organization_webhook_spec.rb @@ -13,9 +13,7 @@ it { should have_many(:organizations) } it { should have_many(:users).through(:organizations) } - it { should validate_uniqueness_of(:github_id).allow_nil } - it { should validate_presence_of(:github_organization_id) } it { should validate_uniqueness_of(:github_organization_id) } @@ -41,6 +39,116 @@ end end + describe "#ensure_webhook_is_active!" do + context "client is nil" do + before do + expect(subject) + .to receive(:admin_org_hook_scoped_github_client) + .and_return(client) + end + + context "github_id is not present" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { false } + end + + it "invokes create_org_hook!" do + expect(subject).to receive(:create_org_hook!).and_return(true) + subject.ensure_webhook_is_active! + end + + it "returns true" do + expect(subject).to receive(:create_org_hook!).and_return(true) + expect(subject.ensure_webhook_is_active!).to be_truthy + end + end + + context "github_org_hook is not active" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + end + + it "invokes create_org_hook!" do + expect(subject).to receive(:create_org_hook!).and_return(true) + subject.ensure_webhook_is_active! + end + + it "returns true" do + expect(subject).to receive(:create_org_hook!).and_return(true) + expect(subject.ensure_webhook_is_active!).to be_truthy + end + end + + context "github_org_hook is active" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { true } + end + + it "does not invoke create_org_hook!" do + expect(subject).to_not receive(:create_org_hook!) + subject.ensure_webhook_is_active! + end + + it "returns true" do + expect(subject.ensure_webhook_is_active!).to be_truthy + end + end + end + + context "client is present" do + context "github_id is not present" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { false } + end + + it "invokes create_org_hook!" do + expect(subject).to receive(:create_org_hook!).and_return(true) + subject.ensure_webhook_is_active!(client: client) + end + + it "returns true" do + expect(subject).to receive(:create_org_hook!).and_return(true) + expect(subject.ensure_webhook_is_active!(client: client)).to be_truthy + end + end + + context "github_org_hook is not active" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + end + + it "invokes create_org_hook!" do + expect(subject).to receive(:create_org_hook!).and_return(true) + subject.ensure_webhook_is_active!(client: client) + end + + it "returns true" do + expect(subject).to receive(:create_org_hook!).and_return(true) + expect(subject.ensure_webhook_is_active!(client: client)).to be_truthy + end + end + + context "github_org_hook is active" do + before do + expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { true } + end + + it "does not invoke create_org_hook!" do + expect(subject).to_not receive(:create_org_hook!) + subject.ensure_webhook_is_active!(client: client) + end + + it "returns true" do + expect(subject.ensure_webhook_is_active!(client: client)).to be_truthy + end + end + end + end + describe "#create_org_hook!", :vcr do context "GitHub::Error is raised" do before do @@ -50,7 +158,7 @@ end it "raises a GitHub::Error" do - expect { subject.create_org_hook!(client: client) } + expect { subject.create_org_hook!(client) } .to raise_error(GitHub::Error) end end @@ -65,7 +173,7 @@ end it "raises a ActiveRecord::RecordInvalid" do - expect { subject.create_org_hook!(client: client) } + expect { subject.create_org_hook!(client) } .to raise_error(ActiveRecord::RecordInvalid) end end @@ -77,7 +185,7 @@ end it "returns true" do - expect(subject.create_org_hook!(client: client)).to be_truthy + expect(subject.create_org_hook!(client)).to be_truthy end end end @@ -120,7 +228,7 @@ it "returns a valid webhook_url" do expect { subject.send(:webhook_url) } - .to raise_error(RuntimeError, "CLASSROOM_WEBHOOK_URL_PREFIX is not set, please check your .env file") + .to raise_error(RuntimeError, described_class::WEBHOOK_URL_DEVELOPMENT_ERROR) end end end From dd3f2412967ca461f41f1519715761a07198a2cf Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 1 Dec 2018 19:01:46 -0500 Subject: [PATCH 04/11] Added activate_organization_webhook method --- app/models/github_organization.rb | 15 +++++++++++++++ spec/models/github_organization_spec.rb | 15 +++++++++++++++ ...lly_creates_a_GitHub_organization_webhook.json | 1 + 3 files changed, 31 insertions(+) create mode 100644 spec/support/cassettes/GitHubOrganization/_activate_organization_webhook/successfully_creates_a_GitHub_organization_webhook.json diff --git a/app/models/github_organization.rb b/app/models/github_organization.rb index 93d961282c..07a10d2e79 100644 --- a/app/models/github_organization.rb +++ b/app/models/github_organization.rb @@ -119,6 +119,21 @@ def create_organization_webhook(config: {}, options: {}) end end + def activate_organization_webhook(webhook_id, config: {}, options: {}) + GitHub::Errors.with_error_handling do + hook_config = { content_type: "json", secret: webhook_secret }.merge(config) + + hook_options = { + # Send the [wildcard](https://developer.github.com/webhooks/#wildcard-event) + # so that we don't have to upgrade the webhooks everytime we need something new. + events: ["*"], + active: true + }.merge(options) + + @client.edit_org_hook(@id, webhook_id, hook_config, hook_options) + end + end + def organization_webhooks GitHub::Errors.with_error_handling do @client.org_hooks(@id) diff --git a/spec/models/github_organization_spec.rb b/spec/models/github_organization_spec.rb index 0c62835495..1d16c2a34f 100644 --- a/spec/models/github_organization_spec.rb +++ b/spec/models/github_organization_spec.rb @@ -96,6 +96,21 @@ end end + describe "#activate_organization_webhook", :vcr do + before do + @org_hook = @github_organization.create_organization_webhook(config: { url: "http://localhost" }) + end + + after do + @client.remove_org_hook(organization.github_id, @org_hook.id) + end + + it "successfully creates a GitHub organization webhook" do + @github_organization.activate_organization_webhook(@org_hook.id, config: { url: "http://localhost" }) + expect(WebMock).to have_requested(:patch, github_url("/organizations/#{organization.github_id}/hooks/#{@org_hook.id}")) + end + end + describe "#remove_organization_webhook", :vcr do before do @org_hook = @github_organization.create_organization_webhook(config: { url: "http://localhost" }) diff --git a/spec/support/cassettes/GitHubOrganization/_activate_organization_webhook/successfully_creates_a_GitHub_organization_webhook.json b/spec/support/cassettes/GitHubOrganization/_activate_organization_webhook/successfully_creates_a_GitHub_organization_webhook.json new file mode 100644 index 0000000000..a8d20b7653 --- /dev/null +++ b/spec/support/cassettes/GitHubOrganization/_activate_organization_webhook/successfully_creates_a_GitHub_organization_webhook.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.9.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 01 Dec 2018 23:56:18 GMT"],"Content-Type":["application/json; charset=utf-8"],"Transfer-Encoding":["chunked"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4992"],"X-Ratelimit-Reset":["1543711632"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["W/\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["438A:52A0:FA4938:21CEE33:5C031FA2"]},"body":{"encoding":"ASCII-8BIT","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Sat, 01 Dec 2018 23:56:16 GMT"},{"request":{"method":"post","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks","body":{"encoding":"UTF-8","base64_string":"eyJuYW1lIjoid2ViIiwiY29uZmlnIjp7ImNvbnRlbnRfdHlwZSI6Impzb24i\nLCJzZWNyZXQiOiIwZGQ2N2NiZTRkNDE2YjNkNzM3MjIwNzE2Y2VmYmZiODEx\nYzEzN2E0IiwidXJsIjoiaHR0cDovL2xvY2FsaG9zdCJ9LCJldmVudHMiOlsi\nKiJdLCJhY3RpdmUiOnRydWV9\n"},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.9.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 01 Dec 2018 23:56:18 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["409"],"Status":["201 Created"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4991"],"X-Ratelimit-Reset":["1543711632"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"03df863091dabcb2d0c5dbdee8fddca4\""],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"Location":["https://api.github.com/orgs/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_LOGIN\u003e/hooks/66828057"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["4F32:52A0:FA4943:21CEE49:5C031FA2"]},"body":{"encoding":"UTF-8","base64_string":"eyJ0eXBlIjoiT3JnYW5pemF0aW9uIiwiaWQiOjY2ODI4MDU3LCJuYW1lIjoi\nd2ViIiwiYWN0aXZlIjp0cnVlLCJldmVudHMiOlsiKiJdLCJjb25maWciOnsi\nY29udGVudF90eXBlIjoianNvbiIsInNlY3JldCI6IioqKioqKioqIiwidXJs\nIjoiaHR0cDovL2xvY2FsaG9zdCIsImluc2VjdXJlX3NzbCI6IjAifSwidXBk\nYXRlZF9hdCI6IjIwMTgtMTItMDFUMjM6NTY6MThaIiwiY3JlYXRlZF9hdCI6\nIjIwMTgtMTItMDFUMjM6NTY6MThaIiwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0\naHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJ\nT05fR0lUSFVCX0xPR0lOPi9ob29rcy82NjgyODA1NyIsInBpbmdfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcy82NjgyODA1\nNy9waW5ncyJ9\n"},"http_version":null},"recorded_at":"Sat, 01 Dec 2018 23:56:17 GMT"},{"request":{"method":"patch","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks/66828057","body":{"encoding":"UTF-8","base64_string":"eyJjb25maWciOnsiY29udGVudF90eXBlIjoianNvbiIsInNlY3JldCI6IjBk\nZDY3Y2JlNGQ0MTZiM2Q3MzcyMjA3MTZjZWZiZmI4MTFjMTM3YTQiLCJ1cmwi\nOiJodHRwOi8vbG9jYWxob3N0In0sImV2ZW50cyI6WyIqIl0sImFjdGl2ZSI6\ndHJ1ZX0=\n"},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.9.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 01 Dec 2018 23:56:19 GMT"],"Content-Type":["application/json; charset=utf-8"],"Transfer-Encoding":["chunked"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4990"],"X-Ratelimit-Reset":["1543711632"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["W/\"03df863091dabcb2d0c5dbdee8fddca4\""],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["551C:529F:E39FAC:1D84495:5C031FA3"]},"body":{"encoding":"ASCII-8BIT","base64_string":"eyJ0eXBlIjoiT3JnYW5pemF0aW9uIiwiaWQiOjY2ODI4MDU3LCJuYW1lIjoi\nd2ViIiwiYWN0aXZlIjp0cnVlLCJldmVudHMiOlsiKiJdLCJjb25maWciOnsi\nY29udGVudF90eXBlIjoianNvbiIsInNlY3JldCI6IioqKioqKioqIiwidXJs\nIjoiaHR0cDovL2xvY2FsaG9zdCIsImluc2VjdXJlX3NzbCI6IjAifSwidXBk\nYXRlZF9hdCI6IjIwMTgtMTItMDFUMjM6NTY6MThaIiwiY3JlYXRlZF9hdCI6\nIjIwMTgtMTItMDFUMjM6NTY6MThaIiwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0\naHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJ\nT05fR0lUSFVCX0xPR0lOPi9ob29rcy82NjgyODA1NyIsInBpbmdfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcy82NjgyODA1\nNy9waW5ncyJ9\n"},"http_version":null},"recorded_at":"Sat, 01 Dec 2018 23:56:17 GMT"},{"request":{"method":"delete","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks/66828057","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.9.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":204,"message":"No Content"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 01 Dec 2018 23:56:19 GMT"],"Content-Type":["application/octet-stream"],"Status":["204 No Content"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4989"],"X-Ratelimit-Reset":["1543711632"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["135C:529E:7F5810:13752D4:5C031FA3"]},"body":{"encoding":"UTF-8","base64_string":""},"http_version":null},"recorded_at":"Sat, 01 Dec 2018 23:56:17 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file From b9193183ea8f31986a3775752147f5e7a0cba144 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Mon, 3 Dec 2018 13:47:24 -0500 Subject: [PATCH 05/11] Changed ensure_webhook_is_active! to activate an existing webhook if one already exists Fix linter warnings --- app/models/null_github_org_hook.rb | 2 +- app/models/organization_webhook.rb | 25 ++++- spec/models/github_organization_spec.rb | 6 +- spec/models/organization_webhook_spec.rb | 99 ++++++++++++++++--- .../raises_a_GitHub_Error.json | 1 + .../returns_true.json | 1 + .../raises_a_ActiveRecord_RecordInvalid.json | 2 +- .../raises_a_GitHub_Error.json | 2 +- .../returns_true.json | 2 +- 9 files changed, 120 insertions(+), 20 deletions(-) create mode 100644 spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json create mode 100644 spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/org_hook_is_successfully_activated/returns_true.json diff --git a/app/models/null_github_org_hook.rb b/app/models/null_github_org_hook.rb index ccce7176aa..e08082eb51 100644 --- a/app/models/null_github_org_hook.rb +++ b/app/models/null_github_org_hook.rb @@ -2,7 +2,7 @@ class NullGitHubOrgHook < NullGitHubResource def active - false + nil end def active? diff --git a/app/models/organization_webhook.rb b/app/models/organization_webhook.rb index 395a93cdea..fc22ff95eb 100644 --- a/app/models/organization_webhook.rb +++ b/app/models/organization_webhook.rb @@ -24,6 +24,10 @@ def github_org_hook(client) ) end + def github_organization(client) + @github_organization ||= GitHubOrganization.new(client, github_organization_id) + end + # External: Finds a User's token that has the `admin:org_hook` scope # for creating the organization webhook. # @@ -53,14 +57,24 @@ def admin_org_hook_scoped_github_client # # Returns true if successful, otherwise raises a GitHub::Error or ActiveRecord::RecordInvalid. def create_org_hook!(client) - github_organization = GitHubOrganization.new(client, github_organization_id) - github_id = github_organization.create_organization_webhook(config: { url: webhook_url }).id + github_organization(client).create_organization_webhook(config: { url: webhook_url }).id save! rescue ActiveRecord::RecordInvalid => err - github_organization.remove_organization_webhook(github_id) + github_organization(client).remove_organization_webhook(github_id) raise err end + # External: Activates an organization webhook. + # + # client - The client that used to edit the organization webhook + # (Note: client must have the `admin:org_hook` scope). + # + # Returns true if successful, otherwise raises a GitHub::Error + def activate_org_hook(client) + github_organization(client).activate_organization_webhook(github_id, config: { url: webhook_url }) + true + end + # External: Checks if an org hook exists and is active, # otherwise creates one and saves. # @@ -76,7 +90,10 @@ def create_org_hook!(client) # of a large size. Invoke cautiously. def ensure_webhook_is_active!(client: nil) client ||= admin_org_hook_scoped_github_client - create_org_hook!(client) unless github_id.present? && github_org_hook(client).active? + return create_org_hook!(client) if github_id.blank? + github_org_hook_is_active = github_org_hook(client).active? + return create_org_hook!(client) if github_org_hook_is_active.nil? + return activate_org_hook(client) unless github_org_hook_is_active true end diff --git a/spec/models/github_organization_spec.rb b/spec/models/github_organization_spec.rb index 1d16c2a34f..90c07e3e84 100644 --- a/spec/models/github_organization_spec.rb +++ b/spec/models/github_organization_spec.rb @@ -107,7 +107,11 @@ it "successfully creates a GitHub organization webhook" do @github_organization.activate_organization_webhook(@org_hook.id, config: { url: "http://localhost" }) - expect(WebMock).to have_requested(:patch, github_url("/organizations/#{organization.github_id}/hooks/#{@org_hook.id}")) + expect(WebMock) + .to have_requested( + :patch, + github_url("/organizations/#{organization.github_id}/hooks/#{@org_hook.id}") + ) end end diff --git a/spec/models/organization_webhook_spec.rb b/spec/models/organization_webhook_spec.rb index 131898eccd..c42fa1ba8e 100644 --- a/spec/models/organization_webhook_spec.rb +++ b/spec/models/organization_webhook_spec.rb @@ -39,7 +39,7 @@ end end - describe "#ensure_webhook_is_active!" do + describe "#ensure_webhook_is_active!", :vcr do context "client is nil" do before do expect(subject) @@ -49,7 +49,7 @@ context "github_id is not present" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { false } + expect(subject).to receive_message_chain(:github_id, :blank?) { true } end it "invokes create_org_hook!" do @@ -63,10 +63,27 @@ end end - context "github_org_hook is not active" do + context "github_org_hook is not found" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { true } - expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + expect(subject).to receive_message_chain(:github_id, :blank?) { false } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { nil } + end + + it "invokes create_org_hook!" do + expect(subject).to receive(:create_org_hook!).and_return(true) + subject.ensure_webhook_is_active! + end + + it "returns true" do + expect(subject).to receive(:create_org_hook!).and_return(true) + expect(subject.ensure_webhook_is_active!).to be_truthy + end + end + + context "github_org_hook was NotFound" do + before do + expect(subject).to receive_message_chain(:github_id, :blank?) { false } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { nil } end it "invokes create_org_hook!" do @@ -80,9 +97,26 @@ end end + context "github_org_hook is not active" do + before do + expect(subject).to receive_message_chain(:github_id, :blank?) { false } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + end + + it "invokes activate_org_hook!" do + expect(subject).to receive(:activate_org_hook).and_return(true) + subject.ensure_webhook_is_active! + end + + it "returns true" do + expect(subject).to receive(:activate_org_hook).and_return(true) + expect(subject.ensure_webhook_is_active!).to be_truthy + end + end + context "github_org_hook is active" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_id, :blank?) { false } expect(subject).to receive_message_chain(:github_org_hook, :active?) { true } end @@ -100,7 +134,7 @@ context "client is present" do context "github_id is not present" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { false } + expect(subject).to receive_message_chain(:github_id, :blank?) { true } end it "invokes create_org_hook!" do @@ -114,10 +148,10 @@ end end - context "github_org_hook is not active" do + context "github_org_hook was NotFound" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { true } - expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + expect(subject).to receive_message_chain(:github_id, :blank?) { false } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { nil } end it "invokes create_org_hook!" do @@ -131,9 +165,26 @@ end end + context "github_org_hook is not active" do + before do + expect(subject).to receive_message_chain(:github_id, :blank?) { false } + expect(subject).to receive_message_chain(:github_org_hook, :active?) { false } + end + + it "invokes activate_org_hook!" do + expect(subject).to receive(:activate_org_hook).and_return(true) + subject.ensure_webhook_is_active!(client: client) + end + + it "returns true" do + expect(subject).to receive(:activate_org_hook).and_return(true) + expect(subject.ensure_webhook_is_active!(client: client)).to be_truthy + end + end + context "github_org_hook is active" do before do - expect(subject).to receive_message_chain(:github_id, :present?) { true } + expect(subject).to receive_message_chain(:github_id, :blank?) { false } expect(subject).to receive_message_chain(:github_org_hook, :active?) { true } end @@ -190,6 +241,32 @@ end end + describe "#activate_org_hook!", :vcr do + context "GitHub::Error is raised" do + before do + expect_any_instance_of(GitHubOrganization) + .to receive(:activate_organization_webhook) + .and_raise(GitHub::Error) + end + + it "raises a GitHub::Error" do + expect { subject.activate_org_hook(client) } + .to raise_error(GitHub::Error) + end + end + + context "org hook is successfully activated" do + before do + expect_any_instance_of(GitHubOrganization) + .to receive_message_chain(:activate_organization_webhook) { 0 } + end + + it "returns true" do + expect(subject.activate_org_hook(client)).to be_truthy + end + end + end + describe "#users_with_admin_org_hook_scope" do context "user with admin_org hook scope doesn't exist" do before do diff --git a/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json b/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json new file mode 100644 index 0000000000..9f276a719f --- /dev/null +++ b/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 03 Dec 2018 18:45:42 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4965"],"X-Ratelimit-Reset":["1543865426"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DCBA:1580:17EED4B:333A455:5C0579D6"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 03 Dec 2018 18:45:41 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/org_hook_is_successfully_activated/returns_true.json b/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/org_hook_is_successfully_activated/returns_true.json new file mode 100644 index 0000000000..059db02874 --- /dev/null +++ b/spec/support/cassettes/OrganizationWebhook/_activate_org_hook_/org_hook_is_successfully_activated/returns_true.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 03 Dec 2018 18:45:43 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4964"],"X-Ratelimit-Reset":["1543865426"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DCBA:1580:17EED56:333A461:5C0579D6"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 03 Dec 2018 18:45:42 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json index 0e777a1b03..981368dcd1 100644 --- a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json +++ b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json @@ -1 +1 @@ -{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Nov 2018 21:33:29 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4996"],"X-Ratelimit-Reset":["1543530759"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"7ec12b016cc0fbc9836bda2cc945c935\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE73:1580:57C1A8:B9E0C8:5C005B29"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6Nywib3duZWRfcHJpdmF0ZV9yZXBvcyI6NywicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjIs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjozLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Thu, 29 Nov 2018 21:33:29 GMT"},{"request":{"method":"delete","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks/0","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":404,"message":"Not Found"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Nov 2018 21:33:29 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["103"],"Status":["404 Not Found"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4995"],"X-Ratelimit-Reset":["1543530759"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE73:1580:57C1B1:B9E0DC:5C005B29"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiTm90IEZvdW5kIiwiZG9jdW1lbnRhdGlvbl91cmwiOiJo\ndHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzL29yZ3MvaG9va3MvI2Rl\nbGV0ZS1hLWhvb2sifQ==\n"},"http_version":null},"recorded_at":"Thu, 29 Nov 2018 21:33:29 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 03 Dec 2018 18:45:44 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4961"],"X-Ratelimit-Reset":["1543865426"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DCBA:1580:17EEDC8:333A546:5C0579D8"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 03 Dec 2018 18:45:43 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json index 5c4d00e406..ccddf3f466 100644 --- a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json +++ b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/GitHub_Error_is_raised/raises_a_GitHub_Error.json @@ -1 +1 @@ -{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Nov 2018 21:32:39 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4999"],"X-Ratelimit-Reset":["1543530759"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"7ec12b016cc0fbc9836bda2cc945c935\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE6A:1580:57B2A3:B9BFA7:5C005AF6"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6Nywib3duZWRfcHJpdmF0ZV9yZXBvcyI6NywicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjIs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjozLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Thu, 29 Nov 2018 21:32:38 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 03 Dec 2018 18:45:44 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4962"],"X-Ratelimit-Reset":["1543865426"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DCBA:1580:17EEDB2:333A523:5C0579D7"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 03 Dec 2018 18:45:43 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/org_hook_is_successfully_created/returns_true.json b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/org_hook_is_successfully_created/returns_true.json index 28d774ec40..f0c20e94b5 100644 --- a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/org_hook_is_successfully_created/returns_true.json +++ b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/org_hook_is_successfully_created/returns_true.json @@ -1 +1 @@ -{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Nov 2018 21:35:47 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4994"],"X-Ratelimit-Reset":["1543530759"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"7ec12b016cc0fbc9836bda2cc945c935\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE7B:1580:57EDA7:BA3A93:5C005BB3"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6Nywib3duZWRfcHJpdmF0ZV9yZXBvcyI6NywicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjIs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjozLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Thu, 29 Nov 2018 21:35:47 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 03 Dec 2018 18:45:43 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4963"],"X-Ratelimit-Reset":["1543865426"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DCBA:1580:17EED9D:333A47E:5C0579D7"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 03 Dec 2018 18:45:42 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file From 0f6bab8421ac98a4efccaa9eee0af8bca98ebc96 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 15 Dec 2018 15:51:02 -0500 Subject: [PATCH 06/11] Remove check_group_not_previous_acceptee before action --- app/controllers/group_assignment_invitations_controller.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/controllers/group_assignment_invitations_controller.rb b/app/controllers/group_assignment_invitations_controller.rb index 9859fb282f..57136e4736 100644 --- a/app/controllers/group_assignment_invitations_controller.rb +++ b/app/controllers/group_assignment_invitations_controller.rb @@ -9,7 +9,6 @@ class InvalidStatusForRouteError < StandardError; end layout "layouts/invitations" before_action :route_based_on_status, only: %i[setup successful_invitation] - before_action :check_group_not_previous_acceptee, only: :show before_action :check_user_not_group_member, only: :show before_action :check_should_redirect_to_roster_page, only: :show before_action :authorize_group_access, only: :accept_invitation @@ -134,12 +133,6 @@ def authorize_group_access raise NotAuthorized, "You are not permitted to select this team" end - def check_group_not_previous_acceptee - return unless group.present? && group_assignment_repo.present? - - redirect_to successful_invitation_group_assignment_invitation_path - end - def check_user_not_group_member return if group.blank? redirect_to accept_group_assignment_invitation_path From 3bdd4506dd97f3877d0b904e69026f867c1adc82 Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 15 Dec 2018 15:53:15 -0500 Subject: [PATCH 07/11] Fix create_repo logic to only retry if repo_ready? is false, otherwise succeed --- ...group_assignment_invitations_controller.rb | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/controllers/group_assignment_invitations_controller.rb b/app/controllers/group_assignment_invitations_controller.rb index 57136e4736..c859f8678a 100644 --- a/app/controllers/group_assignment_invitations_controller.rb +++ b/app/controllers/group_assignment_invitations_controller.rb @@ -45,14 +45,17 @@ def setup def create_repo job_started = if group_invite_status.accepted? || group_invite_status.errored? - if group_assignment_repo&.github_repository&.empty? + if repo_ready? + group_invite_status.completed! + false + else group_assignment_repo&.destroy @group_assignment_repo = nil + report_retry + group_invite_status.waiting! + GroupAssignmentRepo::CreateGitHubRepositoryJob.perform_later(group_assignment, group, retries: 3) + true end - report_retry - group_invite_status.waiting! - GroupAssignmentRepo::CreateGitHubRepositoryJob.perform_later(group_assignment, group, retries: 3) - true else false end @@ -244,5 +247,11 @@ def organization @organization ||= group_assignment.organization end helper_method :organization + + def repo_ready? + return false unless group_assignment_repo.present? + return false if group_assignment.starter_code? && !group_assignment_repo.github_repository.imported? + true + end end # rubocop:enable Metrics/ClassLength From 771ce1595178313d3d9cd6be308c4b8b6054914d Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 15 Dec 2018 15:53:26 -0500 Subject: [PATCH 08/11] Update tests --- app/models/group_assignment_invitation.rb | 14 +++++------- ..._assignment_invitations_controller_spec.rb | 22 ------------------- .../group_assignment_invitation_spec.rb | 16 ++------------ 3 files changed, 7 insertions(+), 45 deletions(-) diff --git a/app/models/group_assignment_invitation.rb b/app/models/group_assignment_invitation.rb index 13b688566c..7ac9b532b9 100644 --- a/app/models/group_assignment_invitation.rb +++ b/app/models/group_assignment_invitation.rb @@ -75,17 +75,13 @@ def group(repo_access, selected_group, selected_group_title) def group_assignment_repo(invitees_group) group_assignment_params = { group_assignment: group_assignment, group: invitees_group } repo = GroupAssignmentRepo.find_by(group_assignment_params) + invite_status = status(invitees_group) - return Result.success(repo) if repo - - if organization.feature_enabled?(:group_import_resiliency) - invite_status = status(invitees_group) - invite_status.accepted! if invite_status.unaccepted? - return Result.pending + invite_status.accepted! if invite_status.unaccepted? + if repo + Result.success(repo) else - repo = GroupAssignmentRepo.create(group_assignment_params) - return Result.success(repo) if repo - Result.failed("An error has occurred, please refresh the page and try again.") + Result.pending end end # rubocop:enable MethodLength diff --git a/spec/controllers/group_assignment_invitations_controller_spec.rb b/spec/controllers/group_assignment_invitations_controller_spec.rb index 7dee62ea83..1ac0416af1 100644 --- a/spec/controllers/group_assignment_invitations_controller_spec.rb +++ b/spec/controllers/group_assignment_invitations_controller_spec.rb @@ -326,32 +326,10 @@ it "allows user to join" do patch :accept_invitation, params: { id: invitation.key, group: { id: group.id } } - expect(group_assignment.group_assignment_repos.count).to eql(1) expect(student.repo_accesses.count).to eql(1) end end - context "github repository with the same name already exists" do - let(:group) { create(:group, grouping: grouping, github_team_id: 2_973_107) } - - before do - group_assignment_repo = GroupAssignmentRepo.create!(group_assignment: group_assignment, group: group) - @original_repository = organization.github_client.repository(group_assignment_repo.github_repo_id) - group_assignment_repo.delete - patch :accept_invitation, params: { id: invitation.key, group: { id: group.id } } - end - - it "creates a new group assignment repo" do - expect(group_assignment.group_assignment_repos.count).to eql(1) - end - - it "new repository name has expected suffix" do - expect(WebMock) - .to have_requested(:post, %r{#{github_url("/organizations/")}\d+/repos$}) - .with(body: /^.*#{@original_repository.name}-1.*$/) - end - end - context "with group import resiliency enabled" do before do GitHubClassroom.flipper[:group_import_resiliency].enable diff --git a/spec/models/group_assignment_invitation_spec.rb b/spec/models/group_assignment_invitation_spec.rb index 100d39beb8..1a22828c44 100644 --- a/spec/models/group_assignment_invitation_spec.rb +++ b/spec/models/group_assignment_invitation_spec.rb @@ -58,19 +58,7 @@ subject { create(:group_assignment_invitation, group_assignment: group_assignment) } - after(:each) do - RepoAccess.destroy_all - Group.destroy_all - GroupAssignmentRepo.destroy_all - GroupInviteStatus.destroy_all - end - context "success result" do - it "success?" do - result = subject.redeem_for(student, nil, group_name) - expect(result.success?).to be_truthy - end - it "returns the GroupAssignmentRepo" do result = subject.redeem_for(student, nil, group_name) expect(result.group_assignment_repo).to eql(GroupAssignmentRepo.last) @@ -143,12 +131,12 @@ let(:invitation) { create(:group_assignment_invitation) } it "returns a list of invite statuses" do - group_invite_status = GroupInviteStatus.create(group: group, group_assignment_invitation: invitation) + group_invite_status = create(:group_invite_status, group: group, group_assignment_invitation: invitation) expect(invitation.group_invite_statuses).to eq([group_invite_status]) end it "on #destroy destroys invite status and not the group" do - group_invite_status = GroupInviteStatus.create(group: group, group_assignment_invitation: invitation) + group_invite_status = create(:group_invite_status, group: group, group_assignment_invitation: invitation) invitation.destroy expect { group_invite_status.reload }.to raise_error(ActiveRecord::RecordNotFound) expect(group.reload.nil?).to be_falsey From 9ba00528edc0c44e0065cb75122f76d8a5b309cc Mon Sep 17 00:00:00 2001 From: BenEmdon Date: Sat, 15 Dec 2018 15:54:27 -0500 Subject: [PATCH 09/11] Fixed linter warnings --- app/controllers/group_assignment_invitations_controller.rb | 2 +- app/models/group_assignment_invitation.rb | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/controllers/group_assignment_invitations_controller.rb b/app/controllers/group_assignment_invitations_controller.rb index c859f8678a..5eb0afae55 100644 --- a/app/controllers/group_assignment_invitations_controller.rb +++ b/app/controllers/group_assignment_invitations_controller.rb @@ -249,7 +249,7 @@ def organization helper_method :organization def repo_ready? - return false unless group_assignment_repo.present? + return false if group_assignment_repo.blank? return false if group_assignment.starter_code? && !group_assignment_repo.github_repository.imported? true end diff --git a/app/models/group_assignment_invitation.rb b/app/models/group_assignment_invitation.rb index 7ac9b532b9..75333fd32f 100644 --- a/app/models/group_assignment_invitation.rb +++ b/app/models/group_assignment_invitation.rb @@ -71,7 +71,6 @@ def group(repo_access, selected_group, selected_group_title) Group::Creator.perform(title: selected_group_title, grouping: grouping) end - # rubocop:disable MethodLength def group_assignment_repo(invitees_group) group_assignment_params = { group_assignment: group_assignment, group: invitees_group } repo = GroupAssignmentRepo.find_by(group_assignment_params) @@ -84,5 +83,4 @@ def group_assignment_repo(invitees_group) Result.pending end end - # rubocop:enable MethodLength end From beebb31fc859f3089fd1ae189d30da35d5b4fce3 Mon Sep 17 00:00:00 2001 From: ReshefElisha Date: Tue, 18 Dec 2018 16:02:24 -0500 Subject: [PATCH 10/11] use roster id for repo naming more whitespace --- app/controllers/assignments_controller.rb | 4 ++-- app/models/assignment_repo/creator.rb | 12 ++++++++++-- .../assignments/_assignment_form_options.html.erb | 6 ++++++ ...0181220032936_add_use_roster_id_to_assignments.rb | 7 +++++++ db/schema.rb | 3 ++- .../create_github_repository_job_spec.rb | 3 ++- spec/models/assignment_repo/creator_spec.rb | 3 ++- 7 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20181220032936_add_use_roster_id_to_assignments.rb diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb index f917c60547..14c57b561c 100644 --- a/app/controllers/assignments_controller.rb +++ b/app/controllers/assignments_controller.rb @@ -88,7 +88,7 @@ def assistant def new_assignment_params params .require(:assignment) - .permit(:title, :slug, :public_repo, :students_are_repo_admins, :invitations_enabled) + .permit(:title, :slug, :public_repo, :students_are_repo_admins, :invitations_enabled, :use_roster_id) .merge(creator: current_user, organization: @organization, starter_code_repo_id: starter_code_repo_id_param, @@ -130,7 +130,7 @@ def starter_code_repo_id_param def update_assignment_params params .require(:assignment) - .permit(:title, :slug, :public_repo, :students_are_repo_admins, :deadline, :invitations_enabled) + .permit(:title, :slug, :public_repo, :students_are_repo_admins, :deadline, :invitations_enabled, :use_roster_id) .merge(starter_code_repo_id: starter_code_repo_id_param) end diff --git a/app/models/assignment_repo/creator.rb b/app/models/assignment_repo/creator.rb index e54d33d866..896df5247c 100644 --- a/app/models/assignment_repo/creator.rb +++ b/app/models/assignment_repo/creator.rb @@ -197,11 +197,18 @@ def verify_organization_has_private_repos_available! ##################################### # rubocop:disable AbcSize + # rubocop:disable MethodLength def generate_github_repository_name suffix_count = 0 - owner = organization.github_organization.login_no_cache - repository_name = "#{assignment.slug}-#{user.github_user.login_no_cache}" + owner = organization.github_organization.login_no_cache + + entry = RosterEntry.find_by(roster: organization.roster, user: user) + repository_name = if assignment.use_roster_id && entry.present? + "#{assignment.slug}-#{entry.identifier}" + else + "#{assignment.slug}-#{user.github_user.login_no_cache}" + end loop do name = "#{owner}/#{suffixed_repo_name(repository_name, suffix_count)}" @@ -213,6 +220,7 @@ def generate_github_repository_name suffixed_repo_name(repository_name, suffix_count) end # rubocop:enable AbcSize + # rubocop:enable MethodLength def suffixed_repo_name(repository_name, suffix_count) return repository_name if suffix_count.zero? diff --git a/app/views/assignments/_assignment_form_options.html.erb b/app/views/assignments/_assignment_form_options.html.erb index d02fcda468..e41cc27efa 100644 --- a/app/views/assignments/_assignment_form_options.html.erb +++ b/app/views/assignments/_assignment_form_options.html.erb @@ -41,6 +41,12 @@ +
+
+ +
+
+
diff --git a/db/migrate/20181220032936_add_use_roster_id_to_assignments.rb b/db/migrate/20181220032936_add_use_roster_id_to_assignments.rb new file mode 100644 index 0000000000..647d83fd73 --- /dev/null +++ b/db/migrate/20181220032936_add_use_roster_id_to_assignments.rb @@ -0,0 +1,7 @@ +class AddUseRosterIdToAssignments < ActiveRecord::Migration[5.1] + + def change + add_column :assignments, :use_roster_id, :boolean, null: false, default: false + end + +end diff --git a/db/schema.rb b/db/schema.rb index 5cf32ecdc0..b1465a63df 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181101185917) do +ActiveRecord::Schema.define(version: 20181220032936) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -56,6 +56,7 @@ t.string "slug", null: false t.boolean "students_are_repo_admins", default: false, null: false t.boolean "invitations_enabled", default: true + t.boolean "use_roster_id", default: false, null:false t.index ["deleted_at"], name: "index_assignments_on_deleted_at" t.index ["organization_id"], name: "index_assignments_on_organization_id" t.index ["slug"], name: "index_assignments_on_slug" diff --git a/spec/jobs/assignment_repo/create_github_repository_job_spec.rb b/spec/jobs/assignment_repo/create_github_repository_job_spec.rb index 69f399cf01..08dc6370db 100644 --- a/spec/jobs/assignment_repo/create_github_repository_job_spec.rb +++ b/spec/jobs/assignment_repo/create_github_repository_job_spec.rb @@ -16,7 +16,8 @@ title: "Learn Elm", starter_code_repo_id: 1_062_897, organization: organization, - students_are_repo_admins: true + students_are_repo_admins: true, + use_roster_id: false } create(:assignment, options) diff --git a/spec/models/assignment_repo/creator_spec.rb b/spec/models/assignment_repo/creator_spec.rb index cc750ffdc8..81569a3285 100644 --- a/spec/models/assignment_repo/creator_spec.rb +++ b/spec/models/assignment_repo/creator_spec.rb @@ -14,7 +14,8 @@ starter_code_repo_id: 1_062_897, organization: organization, students_are_repo_admins: true, - public_repo: false + public_repo: false, + use_roster_id: false } create(:assignment, options) From e974ccccf587836f8f6a239cecc1a6700e317d25 Mon Sep 17 00:00:00 2001 From: ReshefElisha Date: Thu, 20 Dec 2018 13:04:31 -0500 Subject: [PATCH 11/11] no newline at end of file --- .../raises_a_ActiveRecord_RecordInvalid.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json index da94a48e89..45d85ec63c 100644 --- a/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json +++ b/spec/support/cassettes/OrganizationWebhook/_create_org_hook_/ActiveRecord_RecordInvalid_is_raised/raises_a_ActiveRecord_RecordInvalid.json @@ -1 +1 @@ -{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 10 Dec 2018 19:32:46 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4994"],"X-Ratelimit-Reset":["1544472964"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE63:0A8C:11FE2DF:25B463C:5C0EBF5E"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 10 Dec 2018 19:32:46 GMT"},{"request":{"method":"delete","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks/0","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":404,"message":"Not Found"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 10 Dec 2018 19:32:46 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["103"],"Status":["404 Not Found"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4993"],"X-Ratelimit-Reset":["1544472964"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE63:0A8C:11FE2EE:25B4653:5C0EBF5E"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiTm90IEZvdW5kIiwiZG9jdW1lbnRhdGlvbl91cmwiOiJo\ndHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzL29yZ3MvaG9va3MvI2Rl\nbGV0ZS1hLWhvb2sifQ==\n"},"http_version":null},"recorded_at":"Mon, 10 Dec 2018 19:32:46 GMT"}],"recorded_with":"VCR 3.0.3"} +{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 10 Dec 2018 19:32:46 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["1435"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4994"],"X-Ratelimit-Reset":["1544472964"],"Cache-Control":["private, max-age=60, s-maxage=60"],"Vary":["Accept, Authorization, Cookie, X-GitHub-OTP"],"Etag":["\"62637ff59620967aa2e63f9474a5ef2c\""],"Last-Modified":["Mon, 12 Nov 2018 18:17:18 GMT"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org, read:org, repo, user, write:org"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE63:0A8C:11FE2DF:25B463C:5C0EBF5E"]},"body":{"encoding":"UTF-8","base64_string":"eyJsb2dpbiI6IjxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05f\nR0lUSFVCX0xPR0lOPiIsImlkIjo8VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9JRD4sIm5vZGVfaWQiOiJNREV5T2s5eVoyRnVh\nWHBoZEdsdmJqUXdNakV6TlRNeSIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JHQU5JWkFUSU9O\nX0dJVEhVQl9MT0dJTj4iLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRo\ndWIuY29tL29yZ3MvPFRFU1RfQ0xBU1NST09NX09XTkVSX09SR0FOSVpBVElP\nTl9HSVRIVUJfTE9HSU4+L3JlcG9zIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8v\nYXBpLmdpdGh1Yi5jb20vb3Jncy88VEVTVF9DTEFTU1JPT01fT1dORVJfT1JH\nQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4vZXZlbnRzIiwiaG9va3NfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9ob29rcyIsImlzc3Vl\nc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL29yZ3MvPFRFU1RfQ0xB\nU1NST09NX09XTkVSX09SR0FOSVpBVElPTl9HSVRIVUJfTE9HSU4+L2lzc3Vl\ncyIsIm1lbWJlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdz\nLzxURVNUX0NMQVNTUk9PTV9PV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xP\nR0lOPi9tZW1iZXJzey9tZW1iZXJ9IiwicHVibGljX21lbWJlcnNfdXJsIjoi\naHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzLzxURVNUX0NMQVNTUk9PTV9P\nV05FUl9PUkdBTklaQVRJT05fR0lUSFVCX0xPR0lOPi9wdWJsaWNfbWVtYmVy\nc3svbWVtYmVyfSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMzLmdp\ndGh1YnVzZXJjb250ZW50LmNvbS91LzxURVNUX0NMQVNTUk9PTV9PV05FUl9P\nUkdBTklaQVRJT05fR0lUSFVCX0lEPj92PTQiLCJkZXNjcmlwdGlvbiI6bnVs\nbCwibmFtZSI6bnVsbCwiY29tcGFueSI6bnVsbCwiYmxvZyI6bnVsbCwibG9j\nYXRpb24iOm51bGwsImVtYWlsIjpudWxsLCJpc192ZXJpZmllZCI6ZmFsc2Us\nImhhc19vcmdhbml6YXRpb25fcHJvamVjdHMiOnRydWUsImhhc19yZXBvc2l0\nb3J5X3Byb2plY3RzIjp0cnVlLCJwdWJsaWNfcmVwb3MiOjMxLCJwdWJsaWNf\nZ2lzdHMiOjAsImZvbGxvd2VycyI6MCwiZm9sbG93aW5nIjowLCJodG1sX3Vy\nbCI6Imh0dHBzOi8vZ2l0aHViLmNvbS88VEVTVF9DTEFTU1JPT01fT1dORVJf\nT1JHQU5JWkFUSU9OX0dJVEhVQl9MT0dJTj4iLCJjcmVhdGVkX2F0IjoiMjAx\nOC0wNi0xMlQxODowNToxMloiLCJ1cGRhdGVkX2F0IjoiMjAxOC0xMS0xMlQx\nODoxNzoxOFoiLCJ0eXBlIjoiT3JnYW5pemF0aW9uIiwidG90YWxfcHJpdmF0\nZV9yZXBvcyI6NSwib3duZWRfcHJpdmF0ZV9yZXBvcyI6NSwicHJpdmF0ZV9n\naXN0cyI6MCwiZGlza191c2FnZSI6NjY1ODgsImNvbGxhYm9yYXRvcnMiOjEs\nImJpbGxpbmdfZW1haWwiOiJiZW5lbWRvbkBnaXRodWIuY29tIiwicGxhbiI6\neyJuYW1lIjoieXR0ZXJiaXVtIiwic3BhY2UiOjk3NjU2MjQ5OSwicHJpdmF0\nZV9yZXBvcyI6MTM1MCwiZmlsbGVkX3NlYXRzIjoyLCJzZWF0cyI6NX0sImRl\nZmF1bHRfcmVwb3NpdG9yeV9wZXJtaXNzaW9uIjoibm9uZSIsIm1lbWJlcnNf\nY2FuX2NyZWF0ZV9yZXBvc2l0b3JpZXMiOnRydWUsInR3b19mYWN0b3JfcmVx\ndWlyZW1lbnRfZW5hYmxlZCI6ZmFsc2V9\n"},"http_version":null},"recorded_at":"Mon, 10 Dec 2018 19:32:46 GMT"},{"request":{"method":"delete","uri":"https://api.github.com/organizations/\u003cTEST_CLASSROOM_OWNER_ORGANIZATION_GITHUB_ID\u003e/hooks/0","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"User-Agent":["Octokit Ruby Gem 4.9.0"],"Accept":["application/vnd.github.v3+json"],"Content-Type":["application/json"],"Expect":[""]}},"response":{"status":{"code":404,"message":"Not Found"},"headers":{"Server":["GitHub.com"],"Date":["Mon, 10 Dec 2018 19:32:46 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["103"],"Status":["404 Not Found"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4993"],"X-Ratelimit-Reset":["1544472964"],"X-Oauth-Scopes":["admin:org, admin:org_hook, delete_repo, repo, user:email"],"X-Accepted-Oauth-Scopes":["admin:org_hook"],"X-Oauth-Client-Id":["\u003cTEST_APPLICATION_GITHUB_CLIENT_ID\u003e"],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type"],"Access-Control-Allow-Origin":["*"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Frame-Options":["deny"],"X-Content-Type-Options":["nosniff"],"X-Xss-Protection":["1; mode=block"],"Referrer-Policy":["origin-when-cross-origin, strict-origin-when-cross-origin"],"Content-Security-Policy":["default-src 'none'"],"X-Github-Request-Id":["DE63:0A8C:11FE2EE:25B4653:5C0EBF5E"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiTm90IEZvdW5kIiwiZG9jdW1lbnRhdGlvbl91cmwiOiJo\ndHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzL29yZ3MvaG9va3MvI2Rl\nbGV0ZS1hLWhvb2sifQ==\n"},"http_version":null},"recorded_at":"Mon, 10 Dec 2018 19:32:46 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file