[deploy] Decouple GitHub authentication from fetching GitHub issues (#7660)

* Improve error handling

* Add tests for Github::Client

* Add GithubIssue validation

* Refactor GithubIssue and fix bug with pull requests

* Improve tests

* Fix PR API URL generation

* Improve logic and get rid of the issue fragment

* Fix disappeared avatar

* Improve rendering

* Activate client side caching using Redis

* Remove unnecessary file

* Add RemoveGithubIssues data update script

* Fix comma

* Forgot to disable the approvals verify text

* Apply PR feedback
This commit is contained in:
rhymes 2020-05-04 22:17:16 +02:00 committed by GitHub
parent f75bcf8673
commit ae8a58fec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1177 additions and 357 deletions

View file

@ -48,8 +48,8 @@ variable :FORCE_SSL_IN_RAILS, :String, default: "not applicable in dev"
# Github for github related access
# (https://developer.github.com/v3/guides/basics-of-authentication/)
variable :GITHUB_KEY, :String, default: "Optional"
variable :GITHUB_SECRET, :String, default: "Optional"
variable :GITHUB_KEY, :String, default: ""
variable :GITHUB_SECRET, :String, default: ""
# Twitter for normal twitter access
# (https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens)

View file

@ -38,6 +38,7 @@ gem "elasticsearch", "~> 7.6" # Powers DEVs core search functionality
gem "email_validator", "~> 2.0" # Email validator for Rails and ActiveModel
gem "emoji_regex", "~> 3.0" # A pair of Ruby regular expressions for matching Unicode Emoji symbols
gem "envied", "~> 0.9" # Ensure presence and type of your app's ENV-variables
gem "faraday-http-cache", "~> 2.2" # Middleware to handle HTTP caching
gem "fast_jsonapi", "~> 1.5" # Serializer for Ruby objects
gem "fastly", "~> 2.5" # Client library for the Fastly acceleration system
gem "feedjira", "~> 3.1" # A feed fetching and parsing library

View file

@ -299,6 +299,8 @@ GEM
i18n (>= 1.6, < 2)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
faraday-http-cache (2.2.0)
faraday (>= 0.8)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fast_jsonapi (1.5)
@ -880,6 +882,7 @@ DEPENDENCIES
exifr (>= 1.3.6)
factory_bot_rails (~> 5.2)
faker (~> 2.11)
faraday-http-cache (~> 2.2)
fast_jsonapi (~> 1.5)
fastly (~> 2.5)
feedjira (~> 3.1)

View file

@ -1,4 +1,5 @@
@import 'variables';
.ltag_github-liquid-tag {
margin: 1.1em auto 1.3em;
max-width: 580px;
@ -95,19 +96,19 @@
display: block;
}
img.github-liquid-tag-img {
.github-liquid-tag-img {
min-height: inherit;
width: 44px;
height: 44px;
width: 38px;
height: 38px;
border-radius: 3px;
float: none;
padding: 10px 0 10px 12px;
padding: 10px 12px 10px 0px;
margin: inherit;
@media screen and (min-width: 500px) {
float: left;
padding: 0;
margin-left: -75px;
margin-left: -9px;
}
}
}

View file

@ -1,19 +1,22 @@
# GithubTag generates the following api links
# GithubTag generates the following API links:
# getting an issue
# https://api.github.com/repos/facebook/react/issues/9218
# getting comments of an issue
# https://api.github.com/repos/facebook/react/issues/9218/comments
# getting exat comment of an issue
# getting a pull request
# https://api.github.com/repos/facebook/react/pulls/14105
# getting a comment belonging to an issue
# https://api.github.com/repos/facebook/react/issues/comments/287635042
class GithubTag
class GithubIssueTag
PARTIAL = "liquids/github_issue".freeze
API_BASE_ENDPOINT = "https://api.github.com/repos/".freeze
def initialize(link)
@orig_link = link
@link = parse_link(link)
@content = GithubIssue.find_or_fetch(@link)
@content_json = @content.issue_serialized
@is_issue = @content_json[:title].present?
@created_at = @content_json[:created_at]
@body = @content.processed_html.html_safe
end
@ -21,20 +24,24 @@ class GithubTag
ActionController::Base.new.render_to_string(
partial: PARTIAL,
locals: {
title: @content_json[:title],
issue_number: @content_json[:number],
user_html_url: @content_json[:user][:html_url],
body: @body,
created_at: @created_at.rfc3339,
date: @created_at.utc.strftime("%b %d, %Y"),
html_url: @content_json[:html_url],
issue_number: issue_number,
tagline: tagline,
title: title,
user_avatar_url: @content_json[:user][:avatar_url],
username: @content_json[:user][:login],
date_link: @content_json[:html_url],
date: Time.zone.parse(@content_json[:created_at].to_s).utc.strftime("%b %d, %Y"),
body: @body
user_html_url: @content_json[:user][:html_url],
username: @content_json[:user][:login]
},
)
end
private
attr_reader :content_json
def parse_link(link)
link = ActionController::Base.helpers.strip_tags(link)
link_no_space = link.delete(" ")
@ -46,9 +53,29 @@ class GithubTag
end
def generate_api_link(input)
input = input.gsub(/\?.*/, "")
input = input.gsub(/\d{1,}#issuecomment-/, "comments/") if input.include?("#issuecomment-")
"https://api.github.com/repos/#{input.gsub(/.*github\.com\//, '')}"
uri = URI.parse(input).normalize
uri.host = nil if uri.host == "github.com"
# public PRs URLs are "/pull/{id}" but the API requires "/pulls/{id}"
uri.path = uri.path.gsub(%r{/pull/}, "/pulls/")
# public comments URLs are "/issues/{id}#issuecomment-{comment_id}"
# or "/pull/{id}#issuecomment-{comment_id}"
# but the API requires "/issues/comments/{comment_id}"
if uri.fragment&.start_with?("issuecomment-")
uri.path = uri.path.gsub(%r{(issues|pulls)/\d+}, "issues/comments/")
comment_id = uri.fragment.split("-").last
uri.merge!(comment_id)
end
# fragments and query params are not needed in the API call
uri.fragment = nil
uri.query = nil
# remove leading forward slash in the path
path = uri.path.sub(%r{\A/}, "")
URI.parse(API_BASE_ENDPOINT).merge(path).to_s
end
def finalize_html(input)
@ -68,7 +95,19 @@ class GithubTag
end
def raise_error
raise StandardError, "Invalid Github issue link"
raise StandardError, "Invalid GitHub issue, pull request or comment link"
end
def title
content_json[:title] || "Comment for"
end
def issue_number
@issue_number ||= content_json[:number] || content_json[:issue_url].split("/").last
end
def tagline
@is_issue ? "posted on" : "commented on"
end
end
end

View file

@ -1,65 +1,78 @@
# NOTE: we are using `GithubIssue` to store issues, pull requests and comments
class GithubIssue < ApplicationRecord
CATEGORIES = %w[issue issue_comment].freeze
API_URL_REGEXP = %r{\Ahttps://api.github.com/repos/.*\z}.freeze
PATH_COMMENT_REGEXP = %r{/issues/comments/}.freeze
PATH_ISSUE_REGEXP = %r{/issues/}.freeze
PATH_PULL_REQUEST_REGEXP = %r{/pulls/}.freeze
PATH_REPO_REGEXP = %r{.*github.com/repos/}.freeze
serialize :issue_serialized, Hash
validates :category, inclusion: { in: %w[issue issue_comment] }
def self.find_or_fetch(url)
find_by(url: url) || fetch(url)
end
validates :category, inclusion: { in: CATEGORIES }
validates :url, presence: true, length: { maximum: 400 }, format: API_URL_REGEXP
def self.fetch(url)
try_to_get_issue(url)
rescue StandardError => e
raise StandardError, "A GitHub issue 404'ed and could not be found!" if e.message.include?("404 - Not Found")
raise StandardError, e.message
end
def self.try_to_get_issue(url)
client = Octokit::Client.new(access_token: random_token)
issue = GithubIssue.new(url: url)
if /\/issues\/comments/.match?(url)
repo, issue_id = url.gsub(/.*github\.com\/repos\//, "").split("/issues/comments/")
issue.issue_serialized = client.issue_comment(repo, issue_id).to_hash
issue.category = "issue_comment"
else
repo, issue_id = url.gsub(/.*github\.com\/repos\//, "").split(issue_or_pull(url))
issue.issue_serialized = get_issue_serialized(client, repo, issue_id)
issue.category = "issue"
class << self
def find_or_fetch(url)
find_by(url: url) || fetch(url)
end
issue.processed_html = get_html(client, issue)
issue.save!
issue
end
def self.get_html(client, issue)
client.markdown(issue.issue_serialized[:body])
end
private
def self.get_issue_serialized(client, repo, issue_id)
client.issue(repo, issue_id).to_hash
end
def self.random_token
if Rails.env.test?
"ddsddsdsdssdsds"
else
random_identity.token
def fetch(url)
retrieve_and_save_issue(url)
rescue Github::Errors::NotFound => e
raise e, error_message(url)
end
end
def self.random_identity
if Rails.env.production?
Identity.where(provider: "github").last(250).sample
else
Identity.where(provider: "github").last
def retrieve_and_save_issue(url)
issue = new(url: url)
if PATH_COMMENT_REGEXP.match?(url)
repo, issue_id = comment_repo_and_issue_id(url)
issue.issue_serialized = Github::Client.issue_comment(repo, issue_id).to_h
issue.category = "issue_comment"
else
repo, issue_id = issue_or_pull_repo_and_issue_id(url)
issue.issue_serialized = Github::Client.issue(repo, issue_id).to_h
issue.category = "issue"
end
# despite the counter intuitive name `.markdown` returns HTML rendered
# from the original markdown
issue.processed_html = Github::Client.markdown(issue.issue_serialized[:body])
issue.save!
issue
end
end
def self.issue_or_pull(url)
if !url.include?("pull")
"/issues/"
else
"/pull/"
def clean_url(url)
url.gsub(PATH_REPO_REGEXP, "")
end
def comment_repo_and_issue_id(url)
clean_url(url).split(PATH_COMMENT_REGEXP)
end
def issue_or_pull_repo_and_issue_id(url)
splitter = if PATH_PULL_REQUEST_REGEXP.match?(url)
PATH_PULL_REQUEST_REGEXP
else
PATH_ISSUE_REGEXP
end
clean_url(url).split(splitter)
end
def error_message(url)
if PATH_COMMENT_REGEXP.match?(url)
_, issue_id = comment_repo_and_issue_id(url)
"Issue comment #{issue_id} not found"
else
_, issue_id = issue_or_pull_repo_and_issue_id(url)
"Issue #{issue_id} not found"
end
end
end
end

View file

@ -0,0 +1,103 @@
module Github
# Github client (uses ocktokit.rb as a backend)
class Client
class << self
# adapted from https://api.rubyonrails.org/classes/Module.html#method-i-delegate_missing_to
def method_missing(method, *args, &block)
return super unless target.respond_to?(method, false)
# define for re-use
self.class.define_method(method) do |*new_args, &new_block|
request do
target.public_send(method, *new_args, &new_block)
end
end
# call the original method, this will only be called the first time
# as in subsequent calls, the newly defined method will prevail
request do
target.public_send(method, *args, &block)
end
end
# adapted from https://api.rubyonrails.org/classes/Module.html#method-i-delegate_missing_to
def respond_to_missing?(method, _include_all = false)
target.respond_to?(method, false) || super
end
private
def request
Honeycomb.add_field("app.name", "github.client")
yield
rescue Octokit::Error => e
record_error(e)
handle_error(e)
end
def record_error(exception)
class_name = exception.class.name.demodulize
Honeycomb.add_field("github.result", "error")
Honeycomb.add_field("github.error", class_name)
DatadogStatsClient.increment(
"github.errors",
tags: ["error:#{class_name}", "message:#{exception.message}"],
)
end
def handle_error(exception)
class_name = exception.class.name.demodulize
# raise specific error if known, generic one if unknown
error_class = "::Github::Errors::#{class_name}".safe_constantize
raise error_class, exception.message if error_class
error_class = if exception.class < Octokit::ClientError
Github::Errors::ClientError
elsif exception.class < Octokit::ServerError
Github::Errors::ServerError
else
Github::Errors::Error
end
raise error_class, exception.message
end
def target
@target ||= Octokit::Client.new(
client_id: ApplicationConfig["GITHUB_KEY"],
client_secret: ApplicationConfig["GITHUB_SECRET"],
user_agent: "#{Octokit::Default::USER_AGENT} (#{URL.url})",
middleware: faraday_middleware_stack,
connection_options: connection_options,
)
end
def faraday_middleware_stack
# Extending the default functionality
# see <https://github.com/octokit/octokit.rb#advanced-usage>,
# <https://github.com/octokit/octokit.rb#caching>
# and <https://github.com/octokit/octokit.rb/blob/master/lib/octokit/default.rb>
Faraday::RackBuilder.new do |builder|
builder.use Faraday::HttpCache, store: Rails.cache, serializer: Marshal, shared_cache: false
builder.use Faraday::Request::Retry, exceptions: [Octokit::ServerError]
builder.use Octokit::Middleware::FollowRedirects
builder.use Octokit::Response::RaiseError
builder.use Octokit::Response::FeedParser
builder.adapter :patron
end
end
def connection_options
Octokit.connection_options.merge(
request: {
open_timeout: 5,
timeout: 5
},
)
end
end
end
end

View file

@ -0,0 +1,15 @@
module Github
module Errors
class Error < StandardError
end
class ClientError < Error
end
class ServerError < Error
end
class NotFound < ClientError
end
end
end

View file

@ -1,6 +1,6 @@
<div class="ltag_github-liquid-tag">
<h1>
<a href="<%= date_link %>">
<a href="<%= html_url %>">
<img class="github-logo" alt="GitHub logo" src="<%= ActionController::Base.helpers.asset_path('github-logo.svg') %>">
<span class="issue-title">
<%= title %>
@ -18,12 +18,12 @@
<div class="timeline-comment-header-text">
<strong>
<a href="<%= user_html_url %>"><%= username %></a>
</strong> commented on <a href="<%= date_link %>"><%= date %></a><span class="timestamp"></span>
</strong> <%= tagline %> <a href="<%= html_url %>"><time datetime="<%= created_at %>"><%= date %></time></a>
</div>
</div>
<div class="ltag-github-body">
<%= body %>
</div>
<div class="gh-btn-container"><a class="gh-btn" href="<%= date_link %>">View on GitHub</a></div>
<div class="gh-btn-container"><a class="gh-btn" href="<%= html_url %>">View on GitHub</a></div>
</div>
</div>

View file

@ -0,0 +1,7 @@
module DataUpdateScripts
class RemoveGithubIssues
def run
GithubIssue.in_batches.delete_all
end
end
end

View file

@ -0,0 +1,5 @@
FactoryBot.define do
factory :github_issue do
category { "issue" }
end
end

View file

@ -1,40 +1,80 @@
require "rails_helper"
vcr_option = {
cassette_name: "github_api",
allow_playback_repeats: "true"
}
RSpec.describe GithubTag::GithubIssueTag, type: :liquid_tag, vcr: vcr_option do
RSpec.describe GithubTag::GithubIssueTag, type: :liquid_tag, vcr: true do
describe "#id" do
let(:github_link) { "https://github.com/facebook/react/issues/9218" }
let(:url_issue) { "https://github.com/thepracticaldev/dev.to/issues/7434" }
let(:url_issue_fragment) { "https://github.com/thepracticaldev/dev.to/issues/7434#issue-604653303" }
let(:url_pull_request) { "https://github.com/thepracticaldev/dev.to/pull/7653" }
let(:url_pull_request_issue_fragment) { "https://github.com/thepracticaldev/dev.to/pull/7653#issue-412271322" }
let(:url_issue_comment) { "https://github.com/thepracticaldev/dev.to/issues/7434#issuecomment-621043602" }
let(:url_pull_request_comment) { "https://github.com/thepracticaldev/dev.to/pull/7653#issuecomment-622572436" }
let(:url_not_found) { "https://github.com/thepracticaldev/dev.to/issues/0" }
setup { Liquid::Template.register_tag("github", GithubTag) }
def generate_github_issue(link)
Liquid::Template.parse("{% github #{link} %}")
def generate_tag(url)
Liquid::Template.register_tag("github", GithubTag)
Liquid::Template.parse("{% github #{url} %}")
end
it "rejects github link without domain" do
it "rejects GitHub URL without domain" do
expect do
generate_github_issue("/react/issues/9193")
generate_tag("/react/issues/9193")
end.to raise_error(StandardError)
end
it "rejects invalid github issue link" do
it "rejects invalid GitHub issue URL" do
expect do
generate_github_issue("https://github.com/issues/9193")
generate_tag("https://github.com/issues/9193")
end.to raise_error(StandardError)
end
xit "renders properly" do
output = generate_github_issue(github_link).render
# NB: this approvals test is a little harder to update
# because a legitimate github access token is required for octokit
# which is then captured via vcr.
# IF YOU DO PLAN TO UPDATE THIS: be sure to remove your access token
# from the vcr cassette generated by this
Approvals.verify(output, name: "github_liquid_tag_default", format: :html)
it "rejects a non existing GitHub issue URL" do
VCR.use_cassette("github_client_issue_not_found") do
expect do
generate_tag(url_not_found)
end.to raise_error(StandardError)
end
end
it "renders an issue URL" do
VCR.use_cassette("github_client_issue") do
html = generate_tag(url_issue).render
expect(html).to include("#7434")
end
end
it "renders an issue URL with an issue fragment" do
VCR.use_cassette("github_client_issue") do
html = generate_tag(url_issue_fragment).render
expect(html).to include("#7434")
end
end
it "renders a pull request URL" do
VCR.use_cassette("github_client_pull_request") do
html = generate_tag(url_pull_request).render
expect(html).to include("#7653")
end
end
it "renders a pull request URL with an issue fragment" do
VCR.use_cassette("github_client_pull_request") do
html = generate_tag(url_pull_request_issue_fragment).render
expect(html).to include("#7653")
end
end
it "renders an issue comment" do
VCR.use_cassette("github_client_comment") do
html = generate_tag(url_issue_comment).render
expect(html).to include("621043602")
end
end
it "renders a PR comment" do
VCR.use_cassette("github_client_pull_request_comment") do
html = generate_tag(url_pull_request_comment).render
expect(html).to include("622572436")
end
end
end
end

View file

@ -1,20 +1,105 @@
require "rails_helper"
vcr_option = {
cassette_name: "github_issue_api",
allow_playback_repeats: "true"
}
RSpec.describe GithubIssue, type: :model, vcr: true do
let(:url_issue) { "https://api.github.com/repos/thepracticaldev/dev.to/issues/7434" }
let(:url_pull_request) { "https://api.github.com/repos/thepracticaldev/dev.to/pulls/7653" }
let(:url_comment) { "https://api.github.com/repos/thepracticaldev/dev.to/issues/comments/621043602" }
let(:url_not_found) { "https://api.github.com/repos/thepracticaldev/dev.to/issues/0" }
RSpec.describe GithubIssue, type: :model, vcr: vcr_option do
let(:link) { "https://api.github.com/repos/thepracticaldev/dev.to/issues/510#issue-354483683" }
it { is_expected.to validate_length_of(:url).is_at_most(400) }
it { is_expected.to validate_inclusion_of(:category).in_array(%w[issue issue_comment]) }
it { is_expected.to validate_presence_of(:url) }
it "finds or fetches based on URL" do
# NB: this approvals test is a little harder to update
# because a legitimate github access token is required for octokit
# which is then captured via vcr.
# IF YOU DO PLAN TO UPDATE THIS: be sure to remove your access token
# from the vcr cassette generated by this
issue = described_class.find_or_fetch(link)
Approvals.verify(issue.processed_html, name: "github_issue_test", format: :html)
describe ".find_or_fetch" do
context "when retrieving an issue", vcr: { cassette_name: "github_client_issue" } do
it "saves a new issue" do
expect do
described_class.find_or_fetch(url_issue)
end.to change(described_class, :count).by(1)
end
it "retrieves an existing issue" do
created_issue = described_class.find_or_fetch(url_issue)
expect do
found_issue = described_class.find_or_fetch(url_issue)
expect(found_issue.id).to eq(created_issue.id)
end.not_to change(described_class, :count)
end
it "saves the proper fields" do
issue = described_class.find_or_fetch(url_issue)
expect(issue.url).to eq(url_issue)
expect(issue.issue_serialized[:title]).to be_present
expect(issue.category).to eq("issue")
end
end
context "when retrieving a pull request", vcr: { cassette_name: "github_client_pull_request" } do
it "saves a new issue" do
expect do
described_class.find_or_fetch(url_pull_request)
end.to change(described_class, :count).by(1)
end
it "retrieves an existing issue" do
created_issue = described_class.find_or_fetch(url_pull_request)
expect do
found_issue = described_class.find_or_fetch(url_pull_request)
expect(found_issue.id).to eq(created_issue.id)
end.not_to change(described_class, :count)
end
it "saves the proper fields" do
issue = described_class.find_or_fetch(url_pull_request)
expect(issue.url).to eq(url_pull_request)
expect(issue.issue_serialized[:title]).to be_present
expect(issue.category).to eq("issue")
end
end
context "when retrieving a comment", vcr: { cassette_name: "github_client_comment" } do
it "saves a new issue comment" do
expect do
described_class.find_or_fetch(url_comment)
end.to change(described_class, :count).by(1)
end
it "retrieves an existing issue comment" do
created_issue = described_class.find_or_fetch(url_comment)
expect do
found_issue = described_class.find_or_fetch(url_comment)
expect(found_issue.id).to eq(created_issue.id)
end.not_to change(described_class, :count)
end
it "saves the proper fields" do
issue = described_class.find_or_fetch(url_comment)
expect(issue.url).to eq(url_comment)
expect(issue.issue_serialized[:title]).not_to be_present
expect(issue.issue_serialized[:body]).to be_present
expect(issue.category).to eq("issue_comment")
end
end
end
xit "saves HTML in .processed_html" do
VCR.use_cassette("github_client_issue") do
issue = described_class.find_or_fetch(url_issue)
Approvals.verify(issue.processed_html, name: "github_client_issue", format: :html)
end
end
it "raises Github::Errors::NotFound if the issue is not found" do
VCR.use_cassette("github_client_issue_not_found") do
expect do
described_class.find_or_fetch(url_not_found)
end.to raise_error(Github::Errors::NotFound)
end
end
end

View file

@ -0,0 +1,22 @@
require "rails_helper"
RSpec.describe Github::Client, type: :service, vcr: true do
let(:repo) { "thepracticaldev/dev.to" }
describe ".issue" do
it "returns a an issue" do
VCR.use_cassette("github_client_issue") do
issue = described_class.issue(repo, 7434)
expect(issue.title).to be_present
end
end
it "raises NotFound if the issue does not exist" do
VCR.use_cassette("github_client_issue_not_found") do
expect do
described_class.issue(repo, 0)
end.to raise_error(Github::Errors::NotFound)
end
end
end
end

View file

@ -1,19 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body><p>@jessleenyc commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638">Tue Jul 31 2018</a></p>
<h1>
<a id="user-content-task-or-feature" class="anchor" href="#task-or-feature" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a><em>TASK or Feature</em>
</h1>
<h3>
<a id="user-content-request-or-user-story" class="anchor" href="#request-or-user-story" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Request or User Story</h3>
<p>Getting lots of crawlers clicking on the hidden link that's giving them 404 errors and jamming up honeybadger!</p>
<h3>
<a id="user-content-definition-of-done" class="anchor" href="#definition-of-done" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Definition of Done</h3>
<h3>
<a id="user-content-notes" class="anchor" href="#notes" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Notes</h3>
<hr /><p>@arun1595 commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-409774531">Wed Aug 01 2018</a></p>
<p>@jessleenyc I would like to tackle this issue. Can you guide me with what "moderate" links are?</p>
<p>Thanks.</p>
<hr /><p>@Zhao-Andy commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-416373282">Mon Aug 27 2018</a></p>
<p>Hey @arun1595, we're going to move this to the public repo and continue the conversation there.</p></body>
</html>

View file

@ -1,45 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<div class="ltag_github-liquid-tag">
<h1>
<a href="https://github.com/facebook/react/issues/9218">
<img class="github-logo" alt="GitHub logo" src="/assets/github-logo-6a5bca60a4ebf959a6df7f08217acd07ac2bc285164fae041eacb8a148b1bab9.svg" /><span class="issue-title">
[Feature] granular forceUpdate
</span>
<span class="issue-number">#9218</span>
</a>
</h1>
<div class="github-thread">
<div class="timeline-comment-header">
<a href="https://github.com/thysultan">
<img class="github-liquid-tag-img" src="https://avatars2.githubusercontent.com/u/810601?v=4" alt="thysultan avatar" /></a>
<span class="arrow-left-outer"></span>
<span class="arrow-left-inner"></span>
<div class="timeline-comment-header-text">
<strong>
<a href="https://github.com/thysultan">thysultan</a>
</strong> commented on <a href="https://github.com/facebook/react/issues/9218">Mar 19, 2017</a><span class="timestamp"></span>
</div>
</div>
<div class="ltag-github-body">
<p>When <code>forceUpdate</code> is called on a long list at the very least the whole list must be traversed. For example with the following list <code>arr = [1, 2, 3, 4, 5, 6]</code> and if we where to push 7 <code>arr.push(7)</code> the reconciler will go through elements 1 - 6 and then add a 7 at the end of that list. The suggestion to add support for the following forceUpdate signature.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-en">forceUpdate</span>(a<span class="pl-k">:</span> {<span class="pl-k">function</span>|number}, b: number?)</pre></div>
<p>Where when passed as an integer <code>a</code> is the start index and <code>b</code> the number of items to traverse similar to how <code>str.substr()</code> works.</p>
<p>When the function encounters a number as the <code>a</code> argument it changes the start index of where to start when reconciling the components children and when the <code>b</code> argument is set it changes the end index of where to end the traversal of the components children. So with the above mentioned example if i wanted to optimize the reconciliation process for append updates i could do the following.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-c1">this</span>.<span class="pl-en">forceUpdate</span>(<span class="pl-smi">arr</span>.<span class="pl-c1">length</span>);</pre></div>
<p>and to target an update in the middle of the list of up to 3 items.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-c1">this</span>.<span class="pl-en">forceUpdate</span>(<span class="pl-smi">arr</span>.<span class="pl-c1">length</span><span class="pl-k">/</span><span class="pl-c1">2</span>, <span class="pl-c1">3</span>);</pre></div>
<p>This will allow for very precise O(1) updates when you know ahead of time how the data will change.</p>
<p>Possible <code>setState</code> counter-part.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-en">setState</span>(a<span class="pl-k">:</span> {<span class="pl-k">function</span>|Object}, b: (<span class="pl-k">function</span>|number)?, c: {number})
<span class="pl-c"><span class="pl-c">//</span> usage</span>
setState({arr<span class="pl-k">:</span> [<span class="pl-k">...</span>]}, <span class="pl-c1">this</span>.<span class="pl-smi">state</span>.<span class="pl-smi">arr</span>.<span class="pl-c1">length</span>);</pre></div>
</div>
<div class="gh-btn-container"><a class="gh-btn" href="https://github.com/facebook/react/issues/9218">View on GitHub</a></div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,143 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/comments/621043602?per_page=100
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.v3+json
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=b7791fdb-dcc2-4751-84d7-6d5b91d4c8d7,parent_id=f98dfdd2-ef73-44bd-a92c-3cc4ffd3f62c,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:45 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '1454'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4978'
X-Ratelimit-Reset:
- '1588465848'
Cache-Control:
- public, max-age=60, s-maxage=60
Vary:
- Accept
- Accept-Encoding, Accept, X-Requested-With
Etag:
- '"1589430fd33ed6b00098c14727a2a9f6"'
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, Deprecation, Sunset
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:
- F3AC:786B:2668DF:351A29:5EAE0845
body:
encoding: ASCII-8BIT
string: '{"url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/comments/621043602","html_url":"https://github.com/thepracticaldev/dev.to/issues/7434#issuecomment-621043602","issue_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7434","id":621043602,"node_id":"MDEyOklzc3VlQ29tbWVudDYyMTA0MzYwMg==","user":{"login":"catalinpit","id":25515812,"node_id":"MDQ6VXNlcjI1NTE1ODEy","avatar_url":"https://avatars3.githubusercontent.com/u/25515812?u=2c7e3d9e557cc819c3dd2069f5e4904d6a365ad1&v=4","gravatar_id":"","url":"https://api.github.com/users/catalinpit","html_url":"https://github.com/catalinpit","followers_url":"https://api.github.com/users/catalinpit/followers","following_url":"https://api.github.com/users/catalinpit/following{/other_user}","gists_url":"https://api.github.com/users/catalinpit/gists{/gist_id}","starred_url":"https://api.github.com/users/catalinpit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/catalinpit/subscriptions","organizations_url":"https://api.github.com/users/catalinpit/orgs","repos_url":"https://api.github.com/users/catalinpit/repos","events_url":"https://api.github.com/users/catalinpit/events{/privacy}","received_events_url":"https://api.github.com/users/catalinpit/received_events","type":"User","site_admin":false},"created_at":"2020-04-29T07:46:28Z","updated_at":"2020-04-29T07:46:28Z","author_association":"CONTRIBUTOR","body":"What
do you think, @ludwiczakpawel?"}'
http_version: null
recorded_at: Sat, 02 May 2020 23:54:45 GMT
- request:
method: post
uri: https://api.github.com/markdown
body:
encoding: UTF-8
string: '{"text":"What do you think, @ludwiczakpawel?"}'
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.raw
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=c3f4fcd3-12df-4e45-90ed-6eff8690d534,parent_id=7666e3c3-e7c8-4655-b4ab-b6f5f9622c88,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:46 GMT
Content-Type:
- text/html;charset=utf-8
Content-Length:
- '43'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4977'
X-Ratelimit-Reset:
- '1588465848'
X-Commonmarker-Version:
- 0.21.0
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, Deprecation, Sunset
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'
Vary:
- Accept-Encoding, Accept, X-Requested-With
X-Github-Request-Id:
- F3AD:786C:193245:227CB0:5EAE0846
body:
encoding: ASCII-8BIT
string: "<p>What do you think, @ludwiczakpawel?</p>\n"
http_version: null
recorded_at: Sat, 02 May 2020 23:54:46 GMT
recorded_with: VCR 5.1.0

View file

@ -0,0 +1,182 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/7434
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.v3+json
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=4085b91d-4649-4fc3-bf1c-96c5a2bc8d57,parent_id=9367c158-33bc-4172-bbbe-b5ac5e8926de,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:39 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '3552'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4983'
X-Ratelimit-Reset:
- '1588465848'
Cache-Control:
- public, max-age=60, s-maxage=60
Vary:
- Accept
- Accept-Encoding, Accept, X-Requested-With
Etag:
- '"54e1b0dd3fed55dc41e2d4affd48d3d7"'
Last-Modified:
- Sat, 02 May 2020 10:56:49 GMT
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, Deprecation, Sunset
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:
- F3A7:29D1:25238A:33C5C5:5EAE083E
body:
encoding: ASCII-8BIT
string: '{"url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7434","repository_url":"https://api.github.com/repos/thepracticaldev/dev.to","labels_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7434/labels{/name}","comments_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7434/comments","events_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7434/events","html_url":"https://github.com/thepracticaldev/dev.to/issues/7434","id":604653303,"node_id":"MDU6SXNzdWU2MDQ2NTMzMDM=","number":7434,"title":"Profile
page not displaying the name due to theming and custom colors","user":{"login":"catalinpit","id":25515812,"node_id":"MDQ6VXNlcjI1NTE1ODEy","avatar_url":"https://avatars0.githubusercontent.com/u/25515812?v=4","gravatar_id":"","url":"https://api.github.com/users/catalinpit","html_url":"https://github.com/catalinpit","followers_url":"https://api.github.com/users/catalinpit/followers","following_url":"https://api.github.com/users/catalinpit/following{/other_user}","gists_url":"https://api.github.com/users/catalinpit/gists{/gist_id}","starred_url":"https://api.github.com/users/catalinpit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/catalinpit/subscriptions","organizations_url":"https://api.github.com/users/catalinpit/orgs","repos_url":"https://api.github.com/users/catalinpit/repos","events_url":"https://api.github.com/users/catalinpit/events{/privacy}","received_events_url":"https://api.github.com/users/catalinpit/received_events","type":"User","site_admin":false},"labels":[{"id":1031106689,"node_id":"MDU6TGFiZWwxMDMxMTA2Njg5","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/accessibility","name":"accessibility","color":"c5aef2","default":false,"description":""},{"id":1290917705,"node_id":"MDU6TGFiZWwxMjkwOTE3NzA1","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/area:%20profile","name":"area:
profile","color":"2a75b2","default":false,"description":"user''s profile"},{"id":1290878626,"node_id":"MDU6TGFiZWwxMjkwODc4NjI2","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/area:%20themes","name":"area:
themes","color":"2a75b2","default":false,"description":"UX theming"},{"id":480869965,"node_id":"MDU6TGFiZWw0ODA4Njk5NjU=","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/type:%20bug","name":"type:
bug","color":"C61C32","default":false,"description":"always open for contribution"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2020-04-22T10:43:38Z","updated_at":"2020-05-02T10:56:49Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"**Describe
the bug**\r\n\r\nYou are allowed to change the colour for some of the information
from your profile page. There are cases when the profile page doesn''t work
properly when the colours are related.\r\n\r\nSee the pictures below or visit
my profile [dev.to/catalinmpit](https://dev.to/catalinmpit).\r\n\r\n**To Reproduce**\r\n\r\n1.
Go to [dev.to/catalinmpit](https://dev.to/catalinmpit)\r\n\r\n**Expected behavior**\r\n\r\nMy
name and other information should be visible.\r\n\r\n**Screenshots**\r\n\r\n![broken-profile-page](https://i.imgur.com/AJyM7tH.png)\r\n\r\n**Aditional
information**\r\n\r\nPlayed around with the settings and I''ve done this:\r\n\r\n![profile-page-ok](https://i.imgur.com/DLiPXFT.png)\r\n\r\n**Question**\r\n\r\nWhat
do you recommend to fix the issue? If you have any recommendations, I''m happy
to pick the issue.\r\n","closed_by":null}'
http_version: null
recorded_at: Sat, 02 May 2020 23:54:39 GMT
- request:
method: post
uri: https://api.github.com/markdown
body:
encoding: UTF-8
string: '{"text":"**Describe the bug**\r\n\r\nYou are allowed to change the
colour for some of the information from your profile page. There are cases
when the profile page doesn''t work properly when the colours are related.\r\n\r\nSee
the pictures below or visit my profile [dev.to/catalinmpit](https://dev.to/catalinmpit).\r\n\r\n**To
Reproduce**\r\n\r\n1. Go to [dev.to/catalinmpit](https://dev.to/catalinmpit)\r\n\r\n**Expected
behavior**\r\n\r\nMy name and other information should be visible.\r\n\r\n**Screenshots**\r\n\r\n![broken-profile-page](https://i.imgur.com/AJyM7tH.png)\r\n\r\n**Aditional
information**\r\n\r\nPlayed around with the settings and I''ve done this:\r\n\r\n![profile-page-ok](https://i.imgur.com/DLiPXFT.png)\r\n\r\n**Question**\r\n\r\nWhat
do you recommend to fix the issue? If you have any recommendations, I''m happy
to pick the issue.\r\n"}'
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.raw
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=63ab979a-3896-4e70-b54d-bf2bf6d375b8,parent_id=b0559d07-130e-4438-ae4a-4852728336a1,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:40 GMT
Content-Type:
- text/html;charset=utf-8
Content-Length:
- '1759'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4982'
X-Ratelimit-Reset:
- '1588465848'
X-Commonmarker-Version:
- 0.21.0
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, Deprecation, Sunset
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'
Vary:
- Accept-Encoding, Accept, X-Requested-With
X-Github-Request-Id:
- F3A8:29D1:2523AC:33C5F9:5EAE0840
body:
encoding: ASCII-8BIT
string: |
<p><strong>Describe the bug</strong></p>
<p>You are allowed to change the colour for some of the information from your profile page. There are cases when the profile page doesn't work properly when the colours are related.</p>
<p>See the pictures below or visit my profile <a href="https://dev.to/catalinmpit" rel="nofollow">dev.to/catalinmpit</a>.</p>
<p><strong>To Reproduce</strong></p>
<ol>
<li>Go to <a href="https://dev.to/catalinmpit" rel="nofollow">dev.to/catalinmpit</a>
</li>
</ol>
<p><strong>Expected behavior</strong></p>
<p>My name and other information should be visible.</p>
<p><strong>Screenshots</strong></p>
<p><a href="https://camo.githubusercontent.com/92af1284df38dd307a8b30ccafbea9f832a82d61/68747470733a2f2f692e696d6775722e636f6d2f414a794d3774482e706e67" target="_blank" rel="nofollow"><img src="https://camo.githubusercontent.com/92af1284df38dd307a8b30ccafbea9f832a82d61/68747470733a2f2f692e696d6775722e636f6d2f414a794d3774482e706e67" alt="broken-profile-page" data-canonical-src="https://i.imgur.com/AJyM7tH.png" style="max-width:100%;"></a></p>
<p><strong>Aditional information</strong></p>
<p>Played around with the settings and I've done this:</p>
<p><a href="https://camo.githubusercontent.com/b2d23465b39fd003c0121c1b87177673d4d96336/68747470733a2f2f692e696d6775722e636f6d2f444c69505846542e706e67" target="_blank" rel="nofollow"><img src="https://camo.githubusercontent.com/b2d23465b39fd003c0121c1b87177673d4d96336/68747470733a2f2f692e696d6775722e636f6d2f444c69505846542e706e67" alt="profile-page-ok" data-canonical-src="https://i.imgur.com/DLiPXFT.png" style="max-width:100%;"></a></p>
<p><strong>Question</strong></p>
<p>What do you recommend to fix the issue? If you have any recommendations, I'm happy to pick the issue.</p>
http_version: null
recorded_at: Sat, 02 May 2020 23:54:40 GMT
recorded_with: VCR 5.1.0

View file

@ -0,0 +1,70 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/0
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.v3+json
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=7b55123d-9904-435d-b5d3-7db505fe3bce,parent_id=73128408-7991-4aac-8a85-f6569b0e9d3b,context=e30=
Expect:
- ''
response:
status:
code: 404
message: Not Found
headers:
Date:
- Sat, 02 May 2020 23:54:41 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '104'
Server:
- GitHub.com
Status:
- 404 Not Found
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4981'
X-Ratelimit-Reset:
- '1588465848'
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, Deprecation, Sunset
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'
Vary:
- Accept-Encoding, Accept, X-Requested-With
X-Github-Request-Id:
- F3A9:29D1:2523C7:33C61F:5EAE0841
body:
encoding: ASCII-8BIT
string: '{"message":"Not Found","documentation_url":"https://developer.github.com/v3/issues/#get-a-single-issue"}'
http_version: null
recorded_at: Sat, 02 May 2020 23:54:41 GMT
recorded_with: VCR 5.1.0

View file

@ -0,0 +1,189 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/7653
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.v3+json
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=3f8011ec-0d23-49d3-9d19-3acdb553274a,parent_id=e31110e6-dc1e-4884-b01b-ee15846d4236,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:43 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '4233'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4980'
X-Ratelimit-Reset:
- '1588465849'
Cache-Control:
- public, max-age=60, s-maxage=60
Vary:
- Accept
- Accept-Encoding, Accept, X-Requested-With
Etag:
- '"0494d7edd146d9a6161dabf46f66cf6e"'
Last-Modified:
- Fri, 01 May 2020 21:47:35 GMT
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, Deprecation, Sunset
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:
- F3AA:4B32:24AC63:32E837:5EAE0842
body:
encoding: ASCII-8BIT
string: '{"url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7653","repository_url":"https://api.github.com/repos/thepracticaldev/dev.to","labels_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7653/labels{/name}","comments_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7653/comments","events_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/7653/events","html_url":"https://github.com/thepracticaldev/dev.to/pull/7653","id":610931120,"node_id":"MDExOlB1bGxSZXF1ZXN0NDEyMjcxMzIy","number":7653,"title":"Log
RateLimit hits to Datadog for Better Reporting","user":{"login":"mstruve","id":1813380,"node_id":"MDQ6VXNlcjE4MTMzODA=","avatar_url":"https://avatars3.githubusercontent.com/u/1813380?v=4","gravatar_id":"","url":"https://api.github.com/users/mstruve","html_url":"https://github.com/mstruve","followers_url":"https://api.github.com/users/mstruve/followers","following_url":"https://api.github.com/users/mstruve/following{/other_user}","gists_url":"https://api.github.com/users/mstruve/gists{/gist_id}","starred_url":"https://api.github.com/users/mstruve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mstruve/subscriptions","organizations_url":"https://api.github.com/users/mstruve/orgs","repos_url":"https://api.github.com/users/mstruve/repos","events_url":"https://api.github.com/users/mstruve/events{/privacy}","received_events_url":"https://api.github.com/users/mstruve/received_events","type":"User","site_admin":false},"labels":[{"id":1123882148,"node_id":"MDU6TGFiZWwxMTIzODgyMTQ4","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/PR:%20merged","name":"PR:
merged","color":"786DBE","default":false,"description":"bot applied label
for PR''s that are merged"}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2020-05-01T19:40:23Z","updated_at":"2020-05-01T21:47:35Z","closed_at":"2020-05-01T21:47:32Z","author_association":"COLLABORATOR","pull_request":{"url":"https://api.github.com/repos/thepracticaldev/dev.to/pulls/7653","html_url":"https://github.com/thepracticaldev/dev.to/pull/7653","diff_url":"https://github.com/thepracticaldev/dev.to/pull/7653.diff","patch_url":"https://github.com/thepracticaldev/dev.to/pull/7653.patch"},"body":"\r\n##
What type of PR is this? (check all applicable)\r\n- [x] Feature\r\n- [x]
Optimization\r\n\r\n## Description\r\nRather than sending (almost) every single
rate limit hit to Slack, lets send a metric to Datadog that we can track and
alert on. The goal here is to send this data to Datadog. Once the data is
there @michael-tharrington can then decide what thresholds he wants to set
for what actions to determine when he gets alerted. Datadog then will be responsible
for sending that information to Slack rather than our app. \r\n\r\nThis will
allow us to set thresholds and alert only when users are excessively hitting
rate limits rather than every single time which sometimes is not concerning
if they only hit it once, like a pentester trying to see if a rate limiter
exists. \r\n\r\n## Related Tickets & Documents\r\nhttps://github.com/thepracticaldev/dev.to/projects/9#card-37397577\r\n\r\n##
Added tests?\r\n- [x] yes\r\n\r\n![alt_text](https://media1.giphy.com/media/l2YWsOymHDRFYqyAM/giphy.gif)\r\n","closed_by":{"login":"mstruve","id":1813380,"node_id":"MDQ6VXNlcjE4MTMzODA=","avatar_url":"https://avatars3.githubusercontent.com/u/1813380?v=4","gravatar_id":"","url":"https://api.github.com/users/mstruve","html_url":"https://github.com/mstruve","followers_url":"https://api.github.com/users/mstruve/followers","following_url":"https://api.github.com/users/mstruve/following{/other_user}","gists_url":"https://api.github.com/users/mstruve/gists{/gist_id}","starred_url":"https://api.github.com/users/mstruve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mstruve/subscriptions","organizations_url":"https://api.github.com/users/mstruve/orgs","repos_url":"https://api.github.com/users/mstruve/repos","events_url":"https://api.github.com/users/mstruve/events{/privacy}","received_events_url":"https://api.github.com/users/mstruve/received_events","type":"User","site_admin":false}}'
http_version: null
recorded_at: Sat, 02 May 2020 23:54:43 GMT
- request:
method: post
uri: https://api.github.com/markdown
body:
encoding: UTF-8
string: '{"text":"\r\n## What type of PR is this? (check all applicable)\r\n-
[x] Feature\r\n- [x] Optimization\r\n\r\n## Description\r\nRather than sending
(almost) every single rate limit hit to Slack, lets send a metric to Datadog
that we can track and alert on. The goal here is to send this data to Datadog.
Once the data is there @michael-tharrington can then decide what thresholds
he wants to set for what actions to determine when he gets alerted. Datadog
then will be responsible for sending that information to Slack rather than
our app. \r\n\r\nThis will allow us to set thresholds and alert only when
users are excessively hitting rate limits rather than every single time which
sometimes is not concerning if they only hit it once, like a pentester trying
to see if a rate limiter exists. \r\n\r\n## Related Tickets & Documents\r\nhttps://github.com/thepracticaldev/dev.to/projects/9#card-37397577\r\n\r\n##
Added tests?\r\n- [x] yes\r\n\r\n![alt_text](https://media1.giphy.com/media/l2YWsOymHDRFYqyAM/giphy.gif)\r\n"}'
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.raw
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=40a6d376-ddbd-4054-8f13-16983a5f5d45,parent_id=8229768b-93f5-4952-8408-fec5650f68a9,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Sat, 02 May 2020 23:54:44 GMT
Content-Type:
- text/html;charset=utf-8
Content-Length:
- '2331'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4979'
X-Ratelimit-Reset:
- '1588465848'
X-Commonmarker-Version:
- 0.21.0
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, Deprecation, Sunset
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'
Vary:
- Accept-Encoding, Accept, X-Requested-With
X-Github-Request-Id:
- F3AB:29D2:192531:22748D:5EAE0843
body:
encoding: ASCII-8BIT
string: |
<h2>
<a id="user-content-what-type-of-pr-is-this-check-all-applicable" class="anchor" href="#what-type-of-pr-is-this-check-all-applicable" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>What type of PR is this? (check all applicable)</h2>
<ul>
<li>[x] Feature</li>
<li>[x] Optimization</li>
</ul>
<h2>
<a id="user-content-description" class="anchor" href="#description" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Description</h2>
<p>Rather than sending (almost) every single rate limit hit to Slack, lets send a metric to Datadog that we can track and alert on. The goal here is to send this data to Datadog. Once the data is there @michael-tharrington can then decide what thresholds he wants to set for what actions to determine when he gets alerted. Datadog then will be responsible for sending that information to Slack rather than our app.</p>
<p>This will allow us to set thresholds and alert only when users are excessively hitting rate limits rather than every single time which sometimes is not concerning if they only hit it once, like a pentester trying to see if a rate limiter exists.</p>
<h2>
<a id="user-content-related-tickets--documents" class="anchor" href="#related-tickets--documents" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Related Tickets &amp; Documents</h2>
<p><a href="https://github.com/thepracticaldev/dev.to/projects/9#card-37397577">https://github.com/thepracticaldev/dev.to/projects/9#card-37397577</a></p>
<h2>
<a id="user-content-added-tests" class="anchor" href="#added-tests" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Added tests?</h2>
<ul>
<li>[x] yes</li>
</ul>
<p><a href="https://camo.githubusercontent.com/3972d99d348007c519ae82451b8c52332933b5f4/68747470733a2f2f6d65646961312e67697068792e636f6d2f6d656469612f6c325957734f796d48445246597179414d2f67697068792e676966" target="_blank" rel="nofollow"><img src="https://camo.githubusercontent.com/3972d99d348007c519ae82451b8c52332933b5f4/68747470733a2f2f6d65646961312e67697068792e636f6d2f6d656469612f6c325957734f796d48445246597179414d2f67697068792e676966" alt="alt_text" data-canonical-src="https://media1.giphy.com/media/l2YWsOymHDRFYqyAM/giphy.gif" style="max-width:100%;"></a></p>
http_version: null
recorded_at: Sat, 02 May 2020 23:54:44 GMT
recorded_with: VCR 5.1.0

View file

@ -0,0 +1,146 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/comments/622572436?per_page=100
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.v3+json
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=2130d678-d18a-4c28-a9d9-148409ed2ce1,parent_id=11aec9b3-1ad8-4021-9807-eb96f9426d36,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Mon, 04 May 2020 09:31:20 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '1663'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4999'
X-Ratelimit-Reset:
- '1588588280'
Cache-Control:
- public, max-age=60, s-maxage=60
Vary:
- Accept
- Accept-Encoding, Accept, X-Requested-With
Etag:
- '"006cef151248d15abe7e14ac18b6c5df"'
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, Deprecation, Sunset
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:
- F1B8:29D1:5382F7:738F6E:5EAFE0E7
body:
encoding: ASCII-8BIT
string: !binary |-
eyJ1cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL3RoZXByYWN0aWNhbGRldi9kZXYudG8vaXNzdWVzL2NvbW1lbnRzLzYyMjU3MjQzNiIsImh0bWxfdXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL3RoZXByYWN0aWNhbGRldi9kZXYudG8vcHVsbC83NjUzI2lzc3VlY29tbWVudC02MjI1NzI0MzYiLCJpc3N1ZV91cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL3RoZXByYWN0aWNhbGRldi9kZXYudG8vaXNzdWVzLzc2NTMiLCJpZCI6NjIyNTcyNDM2LCJub2RlX2lkIjoiTURFeU9rbHpjM1ZsUTI5dGJXVnVkRFl5TWpVM01qUXpOZz09IiwidXNlciI6eyJsb2dpbiI6Im1pY2hhZWwtdGhhcnJpbmd0b24iLCJpZCI6MTYwMDcwNzUsIm5vZGVfaWQiOiJNRFE2VlhObGNqRTJNREEzTURjMSIsImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMyLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzE2MDA3MDc1P3U9ZTVmNDUwNjhhYTYyNDM2NThlYjVkMWQ1MmRiYzhlZWZjOWZlOWE1YiZ2PTQiLCJncmF2YXRhcl9pZCI6IiIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbWljaGFlbC10aGFycmluZ3RvbiIsImh0bWxfdXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL21pY2hhZWwtdGhhcnJpbmd0b24iLCJmb2xsb3dlcnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9taWNoYWVsLXRoYXJyaW5ndG9uL2ZvbGxvd2VycyIsImZvbGxvd2luZ191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL21pY2hhZWwtdGhhcnJpbmd0b24vZm9sbG93aW5ney9vdGhlcl91c2VyfSIsImdpc3RzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbWljaGFlbC10aGFycmluZ3Rvbi9naXN0c3svZ2lzdF9pZH0iLCJzdGFycmVkX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbWljaGFlbC10aGFycmluZ3Rvbi9zdGFycmVkey9vd25lcn17L3JlcG99Iiwic3Vic2NyaXB0aW9uc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL21pY2hhZWwtdGhhcnJpbmd0b24vc3Vic2NyaXB0aW9ucyIsIm9yZ2FuaXphdGlvbnNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9taWNoYWVsLXRoYXJyaW5ndG9uL29yZ3MiLCJyZXBvc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL21pY2hhZWwtdGhhcnJpbmd0b24vcmVwb3MiLCJldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9taWNoYWVsLXRoYXJyaW5ndG9uL2V2ZW50c3svcHJpdmFjeX0iLCJyZWNlaXZlZF9ldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9taWNoYWVsLXRoYXJyaW5ndG9uL3JlY2VpdmVkX2V2ZW50cyIsInR5cGUiOiJVc2VyIiwic2l0ZV9hZG1pbiI6ZmFsc2V9LCJjcmVhdGVkX2F0IjoiMjAyMC0wNS0wMVQyMToxODoxMFoiLCJ1cGRhdGVkX2F0IjoiMjAyMC0wNS0wMVQyMToxODoxMFoiLCJhdXRob3JfYXNzb2NpYXRpb24iOiJDT05UUklCVVRPUiIsImJvZHkiOiJTbyBleGNpdGVkIGZvciB0aGlzISBJJ20gZ29pbmcgdG8gbWFrZSBzdXJlIHdlIHRhbGsgYWJvdXQgdGhpcyBpbiBvdXIgbmV4dCBzdXBwb3J0IG1lZXRpbmcuIEkgdGhpbmsgdGhpcyBpcyBhIHN1cGVyIPCfhpIgdGhpbmcgdG8gbW9uaXRvci4ifQ==
http_version: null
recorded_at: Mon, 04 May 2020 09:31:20 GMT
- request:
method: post
uri: https://api.github.com/markdown
body:
encoding: UTF-8
string: "{\"text\":\"So excited for this! I'm going to make sure we talk about
this in our next support meeting. I think this is a super \U0001F192 thing
to monitor.\"}"
headers:
User-Agent:
- Octokit Ruby Gem 4.18.0 (http://localhost:3000)
Accept:
- application/vnd.github.raw
Content-Type:
- application/json
X-Honeycomb-Trace:
- 1;dataset=,trace_id=a4437cb5-738c-4169-894b-ee27dd425fc1,parent_id=1d39a812-0a36-4737-85bc-8efe8039dbda,context=e30=
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Date:
- Mon, 04 May 2020 09:31:21 GMT
Content-Type:
- text/html;charset=utf-8
Content-Length:
- '278'
Server:
- GitHub.com
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4998'
X-Ratelimit-Reset:
- '1588588280'
X-Commonmarker-Version:
- 0.21.0
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, Deprecation, Sunset
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'
Vary:
- Accept-Encoding, Accept, X-Requested-With
X-Github-Request-Id:
- F1B9:786A:DA23C:12F2DF:5EAFE0E8
body:
encoding: ASCII-8BIT
string: !binary |-
PHA+U28gZXhjaXRlZCBmb3IgdGhpcyEgSSdtIGdvaW5nIHRvIG1ha2Ugc3VyZSB3ZSB0YWxrIGFib3V0IHRoaXMgaW4gb3VyIG5leHQgc3VwcG9ydCBtZWV0aW5nLiBJIHRoaW5rIHRoaXMgaXMgYSBzdXBlciA8Zy1lbW9qaSBjbGFzcz0iZy1lbW9qaSIgYWxpYXM9ImNvb2wiIGZhbGxiYWNrLXNyYz0iaHR0cHM6Ly9naXRodWIuZ2l0aHViYXNzZXRzLmNvbS9pbWFnZXMvaWNvbnMvZW1vamkvdW5pY29kZS8xZjE5Mi5wbmciPvCfhpI8L2ctZW1vamk+IHRoaW5nIHRvIG1vbml0b3IuPC9wPgo=
http_version: null
recorded_at: Mon, 04 May 2020 09:31:21 GMT
recorded_with: VCR 5.1.0

View file

@ -1,180 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api.github.com/repos/thepracticaldev/dev.to/issues/510
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- application/vnd.github.v3+json
User-Agent:
- Octokit Ruby Gem 4.12.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:
- Tue, 25 Sep 2018 19:42:19 GMT
Content-Type:
- application/json; charset=utf-8
Transfer-Encoding:
- chunked
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4993'
X-Ratelimit-Reset:
- '1537907261'
Cache-Control:
- private, max-age=60, s-maxage=60
Vary:
- Accept, Authorization, Cookie, X-GitHub-OTP
Etag:
- W/"cf563de9cf5fd804d0232405d071b71a"
Last-Modified:
- Tue, 04 Sep 2018 21:09:29 GMT
X-Oauth-Scopes:
- public_repo, repo:status
X-Accepted-Oauth-Scopes:
- repo
X-Github-Media-Type:
- github.v3; format=json
Access-Control-Expose-Headers:
- ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
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-Runtime-Rack:
- '0.072510'
X-Github-Request-Id:
- DEF9:675C:3CE1:80FA:5BAA8F9B
body:
encoding: ASCII-8BIT
string: '{"url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/510","repository_url":"https://api.github.com/repos/thepracticaldev/dev.to","labels_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/510/labels{/name}","comments_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/510/comments","events_url":"https://api.github.com/repos/thepracticaldev/dev.to/issues/510/events","html_url":"https://github.com/thepracticaldev/dev.to/issues/510","id":354483683,"node_id":"MDU6SXNzdWUzNTQ0ODM2ODM=","number":510,"title":"Add
to no follow to moderate links","user":{"login":"Zhao-Andy","id":17884966,"node_id":"MDQ6VXNlcjE3ODg0OTY2","avatar_url":"https://avatars0.githubusercontent.com/u/17884966?v=4","gravatar_id":"","url":"https://api.github.com/users/Zhao-Andy","html_url":"https://github.com/Zhao-Andy","followers_url":"https://api.github.com/users/Zhao-Andy/followers","following_url":"https://api.github.com/users/Zhao-Andy/following{/other_user}","gists_url":"https://api.github.com/users/Zhao-Andy/gists{/gist_id}","starred_url":"https://api.github.com/users/Zhao-Andy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Zhao-Andy/subscriptions","organizations_url":"https://api.github.com/users/Zhao-Andy/orgs","repos_url":"https://api.github.com/users/Zhao-Andy/repos","events_url":"https://api.github.com/users/Zhao-Andy/events{/privacy}","received_events_url":"https://api.github.com/users/Zhao-Andy/received_events","type":"User","site_admin":false},"labels":[{"id":1018588332,"node_id":"MDU6TGFiZWwxMDE4NTg4MzMy","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/approved","name":"approved","color":"A3FFBD","default":false},{"id":480869965,"node_id":"MDU6TGFiZWw0ODA4Njk5NjU=","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/bug","name":"bug","color":"ff050d","default":true},{"id":669356684,"node_id":"MDU6TGFiZWw2NjkzNTY2ODQ=","url":"https://api.github.com/repos/thepracticaldev/dev.to/labels/good%20first%20issue","name":"good
first issue","color":"724ac9","default":true}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-08-27T21:23:19Z","updated_at":"2018-09-04T21:09:29Z","closed_at":"2018-09-04T21:09:29Z","author_association":"COLLABORATOR","body":"@jessleenyc
commented on [Tue Jul 31 2018](https://github.com/thepracticaldev/dev.to_core/issues/638)\n\n#
_TASK or Feature_\n### Request or User Story\nGetting lots of crawlers clicking
on the hidden link that''s giving them 404 errors and jamming up honeybadger!\n###
Definition of Done\n\n### Notes\n\n\n\n---\n\n@arun1595 commented on [Wed
Aug 01 2018](https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-409774531)\n\n@jessleenyc
I would like to tackle this issue. Can you guide me with what \"moderate\"
links are?\r\n\r\nThanks.\n\n---\n\n@Zhao-Andy commented on [Mon Aug 27 2018](https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-416373282)\n\nHey
@arun1595, we''re going to move this to the public repo and continue the conversation
there.\n\n","closed_by":{"login":"benhalpern","id":3102842,"node_id":"MDQ6VXNlcjMxMDI4NDI=","avatar_url":"https://avatars0.githubusercontent.com/u/3102842?v=4","gravatar_id":"","url":"https://api.github.com/users/benhalpern","html_url":"https://github.com/benhalpern","followers_url":"https://api.github.com/users/benhalpern/followers","following_url":"https://api.github.com/users/benhalpern/following{/other_user}","gists_url":"https://api.github.com/users/benhalpern/gists{/gist_id}","starred_url":"https://api.github.com/users/benhalpern/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benhalpern/subscriptions","organizations_url":"https://api.github.com/users/benhalpern/orgs","repos_url":"https://api.github.com/users/benhalpern/repos","events_url":"https://api.github.com/users/benhalpern/events{/privacy}","received_events_url":"https://api.github.com/users/benhalpern/received_events","type":"User","site_admin":false}}'
http_version:
recorded_at: Tue, 25 Sep 2018 19:42:19 GMT
- request:
method: post
uri: https://api.github.com/markdown
body:
encoding: UTF-8
string: '{"text":"@jessleenyc commented on [Tue Jul 31 2018](https://github.com/thepracticaldev/dev.to_core/issues/638)\n\n#
_TASK or Feature_\n### Request or User Story\nGetting lots of crawlers clicking
on the hidden link that''s giving them 404 errors and jamming up honeybadger!\n###
Definition of Done\n\n### Notes\n\n\n\n---\n\n@arun1595 commented on [Wed
Aug 01 2018](https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-409774531)\n\n@jessleenyc
I would like to tackle this issue. Can you guide me with what \"moderate\"
links are?\r\n\r\nThanks.\n\n---\n\n@Zhao-Andy commented on [Mon Aug 27 2018](https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-416373282)\n\nHey
@arun1595, we''re going to move this to the public repo and continue the conversation
there.\n\n"}'
headers:
Accept:
- application/vnd.github.raw
User-Agent:
- Octokit Ruby Gem 4.12.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:
- Tue, 25 Sep 2018 19:42:19 GMT
Content-Type:
- text/html;charset=utf-8
Transfer-Encoding:
- chunked
Status:
- 200 OK
X-Ratelimit-Limit:
- '5000'
X-Ratelimit-Remaining:
- '4992'
X-Ratelimit-Reset:
- '1537907261'
X-Commonmarker-Version:
- 0.17.11
Access-Control-Expose-Headers:
- ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
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-Runtime-Rack:
- '0.033681'
X-Github-Request-Id:
- DEFA:675C:3CF7:8124:5BAA8F9B
body:
encoding: ASCII-8BIT
string: |
<p>@jessleenyc commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638">Tue Jul 31 2018</a></p>
<h1>
<a id="user-content-task-or-feature" class="anchor" href="#task-or-feature" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a><em>TASK or Feature</em>
</h1>
<h3>
<a id="user-content-request-or-user-story" class="anchor" href="#request-or-user-story" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Request or User Story</h3>
<p>Getting lots of crawlers clicking on the hidden link that's giving them 404 errors and jamming up honeybadger!</p>
<h3>
<a id="user-content-definition-of-done" class="anchor" href="#definition-of-done" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Definition of Done</h3>
<h3>
<a id="user-content-notes" class="anchor" href="#notes" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Notes</h3>
<hr>
<p>@arun1595 commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-409774531">Wed Aug 01 2018</a></p>
<p>@jessleenyc I would like to tackle this issue. Can you guide me with what "moderate" links are?</p>
<p>Thanks.</p>
<hr>
<p>@Zhao-Andy commented on <a href="https://github.com/thepracticaldev/dev.to_core/issues/638#issuecomment-416373282">Mon Aug 27 2018</a></p>
<p>Hey @arun1595, we're going to move this to the public repo and continue the conversation there.</p>
http_version:
recorded_at: Tue, 25 Sep 2018 19:42:19 GMT
recorded_with: VCR 4.0.0