Add functionality for buffering to different accounts (#752)

* Create initial buffer updates logic

* Add UI functionality and test for sattelite tweets

* Add additional /mod functionality and re-use script partial in /internal
This commit is contained in:
Ben Halpern 2018-09-26 15:51:39 -04:00 committed by GitHub
parent 90c6731184
commit 2517bb555b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 337 additions and 232 deletions

View file

@ -101,6 +101,7 @@ class Internal::ArticlesController < Internal::ApplicationController
where(featured: true).
where("featured_number > ?", Time.now.to_i).
includes(:user).
includes(:buffer_updates).
limited_columns_internal_select.
order("featured_number DESC")
end

View file

@ -3,8 +3,11 @@ class Internal::BufferUpdatesController < Internal::ApplicationController
article = Article.find(params[:article_id])
fb_post = params[:fb_post]
tweet = params[:tweet]
if params[:social_channel] == "twitter"
Bufferizer.new(article, tweet).twitter_post!
if params[:social_channel] == "main_twitter"
Bufferizer.new(article, tweet).main_teet!
render body: nil
elsif params[:social_channel] == "satellite_twitter"
Bufferizer.new(article, tweet).satellite_tweet!
render body: nil
elsif params[:social_channel] == "facebook"
Bufferizer.new(article, fb_post).facebook_post!

View file

@ -28,6 +28,7 @@ class TagDashboard < Administrate::BaseDashboard
alias_for: Field::String,
keywords_for_search: Field::String,
taggings_count: Field::Number,
buffer_profile_id_code: Field::String,
}.freeze
# COLLECTION_ATTRIBUTES
@ -87,6 +88,7 @@ class TagDashboard < Administrate::BaseDashboard
bg_color_hex
text_color_hex
keywords_for_search
buffer_profile_id_code
].freeze
# Overwrite this method to customize how tags are displayed

View file

@ -1,50 +1,42 @@
class Bufferizer
attr_accessor :article, :text
def initialize(article, text)
@article = article
@text = text
end
def twitter_post!
client = Buffer::Client.new(ApplicationConfig["BUFFER_ACCESS_TOKEN"])
client.create_update(
body: {
text:
twitter_buffer_text,
profile_ids: [
ApplicationConfig["BUFFER_TWITTER_ID"],
],
},
)
@article.update(last_buffered: Time.now)
def satellite_tweet!
article.tags.each do |tag|
if tag.buffer_profile_id_code.present?
BufferUpdate.buff!(article.id, twitter_buffer_text, tag.buffer_profile_id_code, "twitter", tag.id)
end
end
article.update(last_buffered: Time.now)
end
def main_teet!
BufferUpdate.buff!(article.id, twitter_buffer_text, ApplicationConfig["BUFFER_TWITTER_ID"], "twitter", nil)
article.update(last_buffered: Time.now)
end
def facebook_post!
client = Buffer::Client.new(ApplicationConfig["BUFFER_ACCESS_TOKEN"])
client.create_update(
body: {
text:
fb_buffer_text,
profile_ids: [
ApplicationConfig["BUFFER_FACEBOOK_ID"], # We're sending to LinkedIn and FB with this.
ApplicationConfig["BUFFER_LINKEDIN_ID"], # That's why there are two profile IDs
],
},
)
@article.update(facebook_last_buffered: Time.now)
BufferUpdate.buff!(article.id, fb_buffer_text, ApplicationConfig["BUFFER_FACEBOOK_ID"], "facebook")
BufferUpdate.buff!(article.id, fb_buffer_text, ApplicationConfig["BUFFER_LINKEDIN_ID"], "linkedin")
article.update(facebook_last_buffered: Time.now)
end
private
def twitter_buffer_text
twit_name = @article.user.twitter_username
if twit_name.present? && @text.size < 245
"#{@text}\n{ author: @#{twit_name} }\nhttps://dev.to#{@article.path}"
twit_name = article.user.twitter_username
if twit_name.present? && text.size < 245
"#{text}\n{ author: @#{twit_name} }\nhttps://dev.to#{article.path}"
else
"#{@text} https://dev.to#{@article.path}"
"#{text} https://dev.to#{article.path}"
end
end
def fb_buffer_text
"#{@text} https://dev.to#{@article.path}"
"#{text} https://dev.to#{article.path}"
end
end

View file

@ -14,6 +14,7 @@ class Article < ApplicationRecord
belongs_to :organization, optional: true
belongs_to :collection, optional: true
has_many :comments, as: :commentable
has_many :buffer_updates
has_many :reactions, as: :reactable, dependent: :destroy
has_many :notifications, as: :notifiable

View file

@ -0,0 +1,41 @@
class BufferUpdate < ApplicationRecord
belongs_to :article
validate :validate_body_text_recent_uniqueness
def self.buff!(article_id, text, buffer_profile_id_code, social_service_name="twitter", tag_id=nil)
buffer_response = send_to_buffer(text, buffer_profile_id_code)
self.create(
article_id: article_id,
tag_id: tag_id,
body_text: text,
buffer_profile_id_code: buffer_profile_id_code,
social_service_name: social_service_name,
buffer_response: buffer_response,
)
end
def self.send_to_buffer(text, buffer_profile_id_code)
client = Buffer::Client.new(ApplicationConfig["BUFFER_ACCESS_TOKEN"])
client.create_update(
body: {
text:
text,
profile_ids: [
buffer_profile_id_code,
],
},
)
end
private
def validate_body_text_recent_uniqueness
if BufferUpdate.where(body_text: body_text, article_id: article_id, tag_id: tag_id, social_service_name: social_service_name).
where("created_at > ?", 2.minutes.ago).any?
errors.add(:body_text, "\"#{body_text}\" has already been submitted very recently")
end
end
end

View file

@ -38,6 +38,12 @@ class Tag < ActsAsTaggableOn::Tag
User.with_role(:tag_moderator, self).order("id ASC").pluck(:id)
end
def self.bufferized_tags
Rails.cache.fetch("bufferized_tags_cache", expires_in: 2.hours) do
where.not(buffer_profile_id_code: nil).pluck(:name)
end
end
private
def evaluate_markdown

View file

@ -52,6 +52,7 @@
<meta name="twitter:title" content="<%=@article.title %>">
<meta name="twitter:description" content="<%=@article.description || "An article from the community" %>">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:widgets:new-embed-design" content="on">
<% if @article.published %>
<meta property="og:image" content="<%= cloud_social_image(@article) %>" />
<meta name="twitter:image:src" content="<%= cloud_social_image(@article) %>">

View file

@ -0,0 +1,89 @@
<script>
function timeNow(objectID){
var seconds = new Date().getTime() / 1000;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
function sink(objectID){
var seconds = new Date().getTime() / 1080;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
$('.row').on("submit", "form", function() {
var form = $(this);
var valuesToSubmit = $(this).serialize();
$.ajax({
type: "POST",
url: $(this).attr('action'), //sumbits it to the given url of the form
data: valuesToSubmit,
// dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
console.log("success")
form.parents(".row").addClass("highlighted-bg")
form.parents(".row").addClass("highlighted-border")
setTimeout(function(){
form.parents(".row").removeClass("highlighted-bg")
},350)
});
return false; // prevents normal behaviour
});
document.getElementById('image-upload-button').onclick = function (e) {
e.preventDefault();
document.getElementById('image-upload').click();
}
document.getElementById('image-upload').onchange = function (e) {
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
document.getElementById('image-upload-file-label').style = 'color:#888888';
document.getElementById('image-upload-file-label').innerHTML = "Uploading...";
document.getElementById('uploaded-image').style = 'display:none';
document.getElementById('image-upload-submit').value = "uploading";
setTimeout(function(){
document.getElementById('image-upload-submit').click(function(){
});
},50)
}
}
document.getElementById('image-upload-submit').onclick = function (e) {
e.preventDefault();
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
var ajaxReq = createAjaxReq();
ajaxReq.onreadystatechange = uploadImageCb.bind(this, ajaxReq);
ajaxReq.open('POST', '/image_uploads', true);
ajaxReq.send(generateUploadFormdata(image));
}
}
function generateUploadFormdata(image) {
var token = document.getElementsByName('authenticity_token')[0].value;
var formData = new FormData();
formData.append('authenticity_token', token);
formData.append('image', image[0]);
return formData;
}
function uploadImageCb(ajaxReq) {
if (ajaxReq.status === 200 && ajaxReq.readyState === XMLHttpRequest.DONE) {
var address = document.getElementById('uploaded-image');
address.value = JSON.parse(ajaxReq.response).link;
address.style.display = "inline-block";
address.style.width = "90%";
address.select();
var uploadedMessage = '';
document.getElementById('image-upload-file-label').innerHTML = uploadedMessage;
document.getElementById('image-upload-file-label').style.color = '#00c673';
document.getElementById('image-upload-submit').style.display = 'none';
}
}
function createAjaxReq() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
</script>

View file

@ -23,7 +23,7 @@
</div>
<% end %>
<b><a href="<%= article.path %>" target="_blank"><%= article.title %></a></b> <a href="<%= article.path %>/edit" target="_blank" style="background:#00da8b;color:white;padding:1px 6px">EDIT</a>
<% article.tag_list.each do |tag| %>
<% article.decorate.cached_tag_list_array.each do |tag| %>
<a href='/t/<%= tag %>'>#<%= tag %></a>
<% end %>
<a href="<%= article.user&.path %>">@<%= article.user&.username %></a> ❤️ <%= article.positive_reactions_count %> | 💬 <%= article.comments_count %> | ID: <a href="/internal/articles/<%= article.id %>"><%= article.id %></a>
@ -51,6 +51,13 @@
&nbsp &nbsp <button class="btn btn-primary">SUBMIT</button>
</form>
<div>
<% article.buffer_updates.order("created_at ASC").each do |buffer_update| %>
<div class="row bg-warning">
<b><a href="https://buffer.com/app/profile/<%= buffer_update.buffer_profile_id_code %>/buffer/queue/list"><%= buffer_update.social_service_name %> <%= " MAIN" if buffer_update.buffer_profile_id_code == ApplicationConfig["BUFFER_TWITTER_ID"] %></a> </b> <%= buffer_update.body_text %> <br/><em style="font-size:0.8em"><%= time_ago_in_words(buffer_update.created_at) %> ago</em>
</div>
<% end %>
</div>
<% if article.boosted_dev_digest_email %>
<br/><br/>
@ -67,27 +74,30 @@
<% unless params[:state]&.include?("classic") %>
<div class="inner-row" style="padding:10px;margin-top:20px;font-weight:800;">
<div>
<%= form_tag "/internal/buffer_updates" do %>
<input type="hidden" name="social_channel" value="twitter" />
<input type="hidden" name="social_channel" value="main_twitter" />
<input type="hidden" name="article_id" value="<%= article.id %>" />
<p> Twitter </p>
<p> Twitter MAIN</p>
<textarea cols="37" rows="6" wrap="hard" name="tweet" maxlength="242" /><%= article.title %></textarea>
<% if article.last_buffered %>
<button class="btn btn-danger" style="font-size:1em;">🙈 TWEETED <%= time_ago_in_words(article.last_buffered) %> ago</button>
<% else %>
<button class="btn btn-info" style="font-size:1em;">😎 TWEET 🚀🚀🚀🚀🚀🚀</button>
<button class="btn btn-info" style="font-size:1em;">🦅 Tweet to @ThePracticalDev</button>
<% end %>
<% if (article.decorate.cached_tag_list_array & Tag.bufferized_tags).any? %>
<%= form_tag "/internal/buffer_updates" do %>
<input type="hidden" name="social_channel" value="satellite_twitter" />
<input type="hidden" name="article_id" value="<%= article.id %>" />
<p> Twitter Satellite</p>
<textarea cols="37" rows="6" wrap="hard" name="tweet" maxlength="242" /><%= article.title %></textarea>
<button class="btn btn-info" style="font-size:1em;">🐦 Tweet to satellites</button>
<% end %>
<% end %>
</div>
<%= form_tag "/internal/buffer_updates" do %>
<input type="hidden" name="social_channel" value="facebook" />
<input type="hidden" name="article_id" value="<%= article.id %>" />
<p> Facebook & LinkedIn </p>
<textarea cols="37" rows="6" wrap="hard" name="fb_post" required/></textarea>
<% if article.facebook_last_buffered %>
<button class="btn btn-danger" style="font-size:1em;">🙈 POSTED <%= time_ago_in_words(article.facebook_last_buffered) %> ago</button>
<% else %>
<button class="btn btn-info" style="font-size:1em;">😎 POST 🚀🚀🚀🚀🚀🚀</button>
<% end %>
<button class="btn btn-info" style="font-size:1em;">📘 Post to Facebook</button>
<% end %>
</div>
<% end %>

View file

@ -60,92 +60,4 @@
<%= paginate @articles %>
</div>
<script>
function timeNow(objectID){
var seconds = new Date().getTime() / 1000;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
function sink(objectID){
var seconds = new Date().getTime() / 1080;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
$('.row').on("submit", "form", function() {
var form = $(this);
var valuesToSubmit = $(this).serialize();
$.ajax({
type: "POST",
url: $(this).attr('action'), //sumbits it to the given url of the form
data: valuesToSubmit,
// dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
console.log("success")
form.parents(".row").addClass("highlighted-bg")
form.parents(".row").addClass("highlighted-border")
setTimeout(function(){
form.parents(".row").removeClass("highlighted-bg")
},350)
});
return false; // prevents normal behaviour
});
document.getElementById('image-upload-button').onclick = function (e) {
e.preventDefault();
document.getElementById('image-upload').click();
}
document.getElementById('image-upload').onchange = function (e) {
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
document.getElementById('image-upload-file-label').style = 'color:#888888';
document.getElementById('image-upload-file-label').innerHTML = "Uploading...";
document.getElementById('uploaded-image').style = 'display:none';
document.getElementById('image-upload-submit').value = "uploading";
setTimeout(function(){
document.getElementById('image-upload-submit').click(function(){
});
},50)
}
}
document.getElementById('image-upload-submit').onclick = function (e) {
e.preventDefault();
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
var ajaxReq = createAjaxReq();
ajaxReq.onreadystatechange = uploadImageCb.bind(this, ajaxReq);
ajaxReq.open('POST', '/image_uploads', true);
ajaxReq.send(generateUploadFormdata(image));
}
}
function generateUploadFormdata(image) {
var token = document.getElementsByName('authenticity_token')[0].value;
var formData = new FormData();
formData.append('authenticity_token', token);
formData.append('image', image[0]);
return formData;
}
function uploadImageCb(ajaxReq) {
if (ajaxReq.status === 200 && ajaxReq.readyState === XMLHttpRequest.DONE) {
var address = document.getElementById('uploaded-image');
address.value = JSON.parse(ajaxReq.response).link;
address.style.display = "inline-block";
address.style.width = "90%";
address.select();
var uploadedMessage = '';
document.getElementById('image-upload-file-label').innerHTML = uploadedMessage;
document.getElementById('image-upload-file-label').style.color = '#00c673';
document.getElementById('image-upload-submit').style.display = 'none';
}
}
function createAjaxReq() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
</script>
<%= render "article_script" %>

View file

@ -1,92 +1,2 @@
<%= render "individual_article", article: @article %>
<script>
function timeNow(objectID){
var seconds = new Date().getTime() / 1000;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
function sink(objectID){
var seconds = new Date().getTime() / 1080;
document.getElementById("featured_number_"+objectID).value = Math.round(seconds);
}
$('.row').on("submit", "form", function() {
var form = $(this);
var valuesToSubmit = $(this).serialize();
$.ajax({
type: "POST",
url: $(this).attr('action'), //sumbits it to the given url of the form
data: valuesToSubmit,
// dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
console.log("success")
form.parents(".row").addClass("highlighted-bg")
form.parents(".row").addClass("highlighted-border")
setTimeout(function(){
form.parents(".row").removeClass("highlighted-bg")
},350)
});
return false; // prevents normal behaviour
});
document.getElementById('image-upload-button').onclick = function (e) {
e.preventDefault();
document.getElementById('image-upload').click();
}
document.getElementById('image-upload').onchange = function (e) {
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
document.getElementById('image-upload-file-label').style = 'color:#888888';
document.getElementById('image-upload-file-label').innerHTML = "Uploading...";
document.getElementById('uploaded-image').style = 'display:none';
document.getElementById('image-upload-submit').value = "uploading";
setTimeout(function(){
document.getElementById('image-upload-submit').click(function(){
});
},50)
}
}
document.getElementById('image-upload-submit').onclick = function (e) {
e.preventDefault();
var image = document.getElementById('image-upload').files;
if (image.length > 0) {
var ajaxReq = createAjaxReq();
ajaxReq.onreadystatechange = uploadImageCb.bind(this, ajaxReq);
ajaxReq.open('POST', '/image_uploads', true);
ajaxReq.send(generateUploadFormdata(image));
}
}
function generateUploadFormdata(image) {
var token = document.getElementsByName('authenticity_token')[0].value;
var formData = new FormData();
formData.append('authenticity_token', token);
formData.append('image', image[0]);
return formData;
}
function uploadImageCb(ajaxReq) {
if (ajaxReq.status === 200 && ajaxReq.readyState === XMLHttpRequest.DONE) {
var address = document.getElementById('uploaded-image');
address.value = JSON.parse(ajaxReq.response).link;
address.style.display = "inline-block";
address.style.width = "90%";
address.select();
var uploadedMessage = '';
document.getElementById('image-upload-file-label').innerHTML = uploadedMessage;
document.getElementById('image-upload-file-label').style.color = '#00c673';
document.getElementById('image-upload-submit').style.display = 'none';
}
}
function createAjaxReq() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
</script>
<%= render "article_script" %>

View file

@ -23,7 +23,7 @@
</style>
<div class="container" style="text-align: center">
<h2>MODERATE: <a href="<%= @moderatable.path %>" rel="nofollow"><%= @moderatable.title %>></a></h2>
<h2>MODERATE: <a href="<%= @moderatable.path %>" rel="nofollow"><%= @moderatable.title %></a></h2>
<button class="reaction-button <%= Reaction.cached_any_reactions_for?(@moderatable, current_user, "thumbsdown") ? "reacted" : "" %>" data-reactable-id="<%= @moderatable.id %>" data-category="thumbsdown" data-reactable-type="<%= @moderatable.class.name %>">
<%= image_tag "emoji/emoji-one-thumbs-down-gray.png" %>
@ -33,12 +33,23 @@
<%= image_tag "emoji/emoji-one-nausea-face-gray.png" %>
<img class="reacted-emoji" src="<%= asset_path("emoji/emoji-one-nausea-face.png") %>"/>
</button>
<p style="width: 90%;margin:50px auto;text-align:left">
<b style="font-size:1.3em">All negative reactions are 100% private.</b>
<br><br>Use the <b>thumbsdown(👎)</b> to move something "down" for any reason (quality, usefulness, code of conduct grey area).
<br><br>The <b>vomit(🤢)</b> is for code of conduct violations (harassment, being an asshole, spam, etc.).
<br><br>The DEV team will be notified about vomits and take appropriate action, but leaving a level-headed comment addressing the behavior is welcome if you feel up for it.
</p>
<% if current_user.has_role?(:super_admin) && @moderatable.class.name == "Article" %>
<h1> Admin Functionality!</h1>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<%= render "internal/articles/individual_article", article: @moderatable %>
<%= render "internal/articles/article_script" %>
<% else %>
<p style="width: 90%;margin:50px auto;text-align:left">
<b style="font-size:1.3em">All negative reactions are 100% private.</b>
<br><br>Use the <b>thumbsdown(👎)</b> to move something "down" for any reason (quality, usefulness, code of conduct grey area).
<br><br>The <b>vomit(🤢)</b> is for code of conduct violations (harassment, being an asshole, spam, etc.).
<br><br>The DEV team will be notified about vomits and take appropriate action, but leaving a level-headed comment addressing the behavior is welcome if you feel up for it.
</p>
<% end %>
</div>
<script defer>

View file

@ -0,0 +1,14 @@
class CreateBufferUpdates < ActiveRecord::Migration[5.1]
def change
create_table :buffer_updates do |t|
t.integer :article_id, null: false
t.integer :tag_id
t.text :body_text
t.string :buffer_profile_id_code
t.string :buffer_id_code
t.string :social_service_name
t.text :buffer_response, default: {}.to_yaml
t.timestamps
end
end
end

View file

@ -0,0 +1,5 @@
class AddBufferProfileIdCodeToTags < ActiveRecord::Migration[5.1]
def change
add_column :tags, :buffer_profile_id_code, :string
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180826174411) do
ActiveRecord::Schema.define(version: 20180924204406) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -163,6 +163,18 @@ ActiveRecord::Schema.define(version: 20180826174411) do
t.string "type_of"
end
create_table "buffer_updates", force: :cascade do |t|
t.integer "article_id", null: false
t.text "body_text"
t.string "buffer_id_code"
t.string "buffer_profile_id_code"
t.text "buffer_response", default: "--- {}\n"
t.datetime "created_at", null: false
t.string "social_service_name"
t.integer "tag_id"
t.datetime "updated_at", null: false
end
create_table "chat_channel_memberships", force: :cascade do |t|
t.bigint "chat_channel_id", null: false
t.datetime "created_at", null: false
@ -388,6 +400,10 @@ ActiveRecord::Schema.define(version: 20180826174411) do
create_table "messages", force: :cascade do |t|
t.bigint "chat_channel_id", null: false
t.datetime "created_at", null: false
t.text "encrypted_message_html"
t.text "encrypted_message_html_iv"
t.text "encrypted_message_markdown"
t.text "encrypted_message_markdown_iv"
t.string "message_html", null: false
t.string "message_markdown", null: false
t.datetime "updated_at", null: false
@ -567,6 +583,7 @@ ActiveRecord::Schema.define(version: 20180826174411) do
create_table "tags", id: :serial, force: :cascade do |t|
t.string "alias_for"
t.string "bg_color_hex"
t.string "buffer_profile_id_code"
t.integer "hotness_score", default: 0
t.string "keywords_for_search"
t.string "name"

View file

@ -6,7 +6,7 @@ RSpec.describe Bufferizer do
it "sends to buffer twitter" do
tweet = "test tweet"
described_class.new(article, tweet).twitter_post!
described_class.new(article, tweet).main_teet!
expect(article.last_buffered.utc.to_i).to be > 2.minutes.ago.to_i
end

View file

@ -0,0 +1,46 @@
require 'rails_helper'
RSpec.describe BufferUpdate, type: :model do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
it "creates update" do
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(1)
end
it "does not allow duplicate updates" do
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(1)
end
it "does not allow duplicate updates if the first one was a little while ago" do
b1 = BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
b1.update_column(:created_at, 5.minutes.ago)
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(2)
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(2)
BufferUpdate.buff!(article.id, "twitter_buffer_text yoyo", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(3)
end
it "allows same text across different social platforms" do
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "facebook")
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter")
expect(BufferUpdate.all.size).to eq(2)
end
it "allows same text across different tags" do
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter", 1)
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter", 2)
expect(BufferUpdate.all.size).to eq(2)
end
it "allows same text across different tags" do
BufferUpdate.buff!(article.id, "twitter_buffer_text", "CODE", "twitter", 1)
BufferUpdate.buff!(create(:article).id, "twitter_buffer_text", "CODE", "twitter", 1)
expect(BufferUpdate.all.size).to eq(2)
end
end

View file

@ -0,0 +1,44 @@
require "rails_helper"
RSpec.describe "InternalBufferUpdates", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
before do
sign_in user
user.add_role(:super_admin)
end
it "creates buffer update for tweet if tweet params are passed" do
post "/internal/buffer_updates",
params:
{ social_channel: "main_twitter", article_id: article.id, tweet: "Hello this is a test" }
expect(BufferUpdate.all.size).to eq(1)
post "/internal/buffer_updates",
params: { social_channel: "main_twitter", article_id: article.id, tweet: "Hello this is a test!" }
expect(BufferUpdate.all.size).to eq(2)
expect(BufferUpdate.last.article_id).to eq(article.id)
end
it "updates last buffered at" do
post "/internal/buffer_updates",
params:
{ social_channel: "main_twitter", article_id: article.id, tweet: "Hello this is a test" }
expect(article.reload.last_buffered).not_to eq(nil)
end
it "updates last buffered at with satellite buffer" do
post "/internal/buffer_updates",
params:
{ social_channel: "satellite_twitter", article_id: article.id, tweet: "Hello this is a test" }
expect(article.reload.last_buffered).not_to eq(nil)
end
it "updates last facebook buffered at" do
post "/internal/buffer_updates",
params:
{ social_channel: "facebook", article_id: article.id, tweet: "Hello this is a test" }
expect(article.reload.facebook_last_buffered).not_to eq(nil)
end
end