Save syndication sources to posts
This commit is contained in:
@@ -11,7 +11,8 @@ module Adamantium
|
||||
delete_post: "commands.posts.delete",
|
||||
undelete_post: "commands.posts.undelete",
|
||||
update_post: "commands.posts.update",
|
||||
syndicate: "commands.posts.syndicate"
|
||||
syndicate: "commands.posts.syndicate",
|
||||
add_post_syndication_source: "commands.posts.add_syndication_source"
|
||||
]
|
||||
|
||||
def handle(req, res)
|
||||
@@ -36,10 +37,13 @@ module Adamantium
|
||||
validation = contract.call(req_entity.to_h)
|
||||
if validation.success?
|
||||
|
||||
url = syndicate.call(validation.to_h) # TODO: set URL on post
|
||||
|
||||
post = command.call(validation.to_h)
|
||||
|
||||
syndicate.call(validation.to_h).bind do |result|
|
||||
source, url = result
|
||||
add_post_syndication_source.call(post.id, source, url)
|
||||
end
|
||||
|
||||
res.status = 201
|
||||
res.headers.merge!(
|
||||
"Location" => "#{settings.micropub_site_url}/#{post.post_type}/#{post.slug}"
|
||||
|
18
app/commands/posts/add_syndication_source.rb
Normal file
18
app/commands/posts/add_syndication_source.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
module Adamantium
|
||||
module Commands
|
||||
module Posts
|
||||
class AddSyndicationSource
|
||||
include Deps["repos.post_repo"]
|
||||
|
||||
def call(post_id, source, url)
|
||||
post = post_repo.find!(post_id).to_h
|
||||
syndication_sources = post[:syndication_sources] || {}
|
||||
syndication_sources[source] = url
|
||||
post[:syndication_sources] = syndication_sources
|
||||
|
||||
post_repo.update(post_id, post)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@@ -1,12 +1,20 @@
|
||||
require "dry/monads"
|
||||
require "dry/monads/do"
|
||||
|
||||
module Adamantium
|
||||
module Commands
|
||||
module Posts
|
||||
class Syndicate
|
||||
include Dry::Monads[:result]
|
||||
include Dry::Monads::Do.for(:call)
|
||||
|
||||
include Deps["settings", "syndication.mastodon"]
|
||||
|
||||
def call(post)
|
||||
if post[:syndicate_to].any? { |url| settings.mastodon_server.match(/#{url}/) }
|
||||
mastodon.call(post: post)
|
||||
url = yield mastodon.call(post: post)
|
||||
|
||||
Success([:mastodon, url])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -8,6 +8,19 @@ module Adamantium
|
||||
module Decorators
|
||||
module Posts
|
||||
class Decorator < SimpleDelegator
|
||||
def syndicated?
|
||||
!syndication_sources.empty?
|
||||
end
|
||||
|
||||
def syndicated_to
|
||||
syndication_sources.map do |source, url|
|
||||
{
|
||||
location: source,
|
||||
url: url
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def prefix_emoji
|
||||
name ? "📝" : "📯"
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
module Adamantium
|
||||
module Repos
|
||||
class PostRepo < Adamantium::Repo[:posts]
|
||||
commands :update
|
||||
commands update: :by_pk
|
||||
|
||||
def create(post_attrs)
|
||||
posts.transaction do
|
||||
@@ -64,6 +64,12 @@ module Adamantium
|
||||
.one!
|
||||
end
|
||||
|
||||
def find!(id)
|
||||
posts
|
||||
.by_pk(id)
|
||||
.one!
|
||||
end
|
||||
|
||||
def slug_exists?(slug)
|
||||
!!posts
|
||||
.where(slug: slug)
|
||||
|
@@ -6,7 +6,14 @@ article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 d
|
||||
|
||||
div class="mb-4 max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600"
|
||||
|
||||
div class="mb-2 max-w-prose mx-auto text-gray-600 dark:text-gray-200 flex"
|
||||
= "Published #{post.display_published_at}"
|
||||
div class="max-w-prose mx-auto text-gray-600 dark:text-gray-200 flex"
|
||||
div class=""
|
||||
= "Published #{post.display_published_at}"
|
||||
span class="text-right flex-1"
|
||||
== render :tags, tags: post.tags
|
||||
div class="mb-2 max-w-prose mx-auto text-gray-600 dark:text-gray-200 flex"
|
||||
- if post.syndicated?
|
||||
span Also on:
|
||||
- post.syndicated_to.each do |loc|
|
||||
a href=loc[:url]
|
||||
== render loc[:location]
|
||||
|
1
app/templates/shared/_mastodon.html.slim
Normal file
1
app/templates/shared/_mastodon.html.slim
Normal file
@@ -0,0 +1 @@
|
||||
<svg class="fill-blue-100 hover:fill-blue-400 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M433 179.11c0-97.2-63.71-125.7-63.71-125.7-62.52-28.7-228.56-28.4-290.48 0 0 0-63.72 28.5-63.72 125.7 0 115.7-6.6 259.4 105.63 289.1 40.51 10.7 75.32 13 103.33 11.4 50.81-2.8 79.32-18.1 79.32-18.1l-1.7-36.9s-36.31 11.4-77.12 10.1c-40.41-1.4-83-4.4-89.63-54a102.54 102.54 0 0 1-.9-13.9c85.63 20.9 158.65 9.1 178.75 6.7 56.12-6.7 105-41.3 111.23-72.9 9.8-49.8 9-121.5 9-121.5zm-75.12 125.2h-46.63v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.33V197c0-58.5-64-56.6-64-6.9v114.2H90.19c0-122.1-5.2-147.9 18.41-175 25.9-28.9 79.82-30.8 103.83 6.1l11.6 19.5 11.6-19.5c24.11-37.1 78.12-34.8 103.83-6.1 23.71 27.3 18.4 53 18.4 175z"/></svg>
|
After Width: | Height: | Size: 910 B |
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ROM::SQL.migration do
|
||||
change do
|
||||
alter_table :posts do
|
||||
add_column :syndication_sources, :json, default: "{}"
|
||||
end
|
||||
end
|
||||
end
|
@@ -1036,6 +1036,14 @@ video {
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.w-8 {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.w-4 {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.max-w-prose {
|
||||
max-width: 65ch;
|
||||
}
|
||||
@@ -1118,6 +1126,10 @@ video {
|
||||
background-color: rgb(254 240 138 / 0.6);
|
||||
}
|
||||
|
||||
.fill-blue-100 {
|
||||
fill: #dbeafe;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
@@ -1331,6 +1343,14 @@ video {
|
||||
background-color: rgb(254 240 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:fill-blue-100:hover {
|
||||
fill: #dbeafe;
|
||||
}
|
||||
|
||||
.hover\:fill-blue-400:hover {
|
||||
fill: #60a5fa;
|
||||
}
|
||||
|
||||
.hover\:text-blue-100:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(219 234 254 / var(--tw-text-opacity));
|
||||
|
37
spec/adamantium/unit/add_syndication_source_spec.rb
Normal file
37
spec/adamantium/unit/add_syndication_source_spec.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Adamantium::Commands::Posts::AddSyndicationSource, :db do
|
||||
subject { described_class.new }
|
||||
|
||||
describe "setting a syndication source" do
|
||||
let(:post) { Test::Factory[:post] }
|
||||
let(:repo) { Adamantium::Container["repos.post_repo"] }
|
||||
|
||||
context "when no sources exist" do
|
||||
it "sets a new source" do
|
||||
subject.call(post.id, "mastodon", "some_url")
|
||||
|
||||
updated_post = repo.find!(post.id)
|
||||
|
||||
expect(updated_post.syndication_sources).to eq({
|
||||
"mastodon" => "some_url"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when a source exists" do
|
||||
let(:post) { Test::Factory[:post, syndication_sources: {"twitter" => "twitter_url"}, slug: "existing-post"] }
|
||||
|
||||
it "adds another source" do
|
||||
subject.call(post.id, "mastodon", "some_url")
|
||||
|
||||
updated_post = repo.find!(post.id)
|
||||
|
||||
expect(updated_post.syndication_sources).to eq({
|
||||
"mastodon" => "some_url",
|
||||
"twitter" => "twitter_url"
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@@ -9,5 +9,4 @@ require "hanami/prepare"
|
||||
require "timecop"
|
||||
|
||||
require_relative "support/rspec"
|
||||
require_relative "support/requests"
|
||||
require_relative "support/db"
|
||||
require_relative "support/feature_loader"
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
require_relative "db/helpers"
|
||||
require_relative "db/database_cleaner"
|
||||
require_relative "db/factory"
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.before :suite do
|
||||
@@ -11,5 +12,5 @@ RSpec.configure do |config|
|
||||
|
||||
config.include Test::DB::Helpers, :db
|
||||
|
||||
# config.include(Test::DB::FactoryHelper.new, factory: nil)
|
||||
config.include(Test::DB::FactoryHelper.new, factory: nil)
|
||||
end
|
||||
|
@@ -5,6 +5,10 @@ require_relative "helpers"
|
||||
DatabaseCleaner[:sequel].strategy = :transaction
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.before :suite do
|
||||
DatabaseCleaner[:sequel].clean_with :truncation
|
||||
end
|
||||
|
||||
config.prepend_before :each, type: :db do |example|
|
||||
strategy = example.metadata[:js] ? :truncation : :transaction
|
||||
DatabaseCleaner[:sequel].strategy = strategy
|
||||
|
42
spec/support/db/factory.rb
Normal file
42
spec/support/db/factory.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
require "rom-factory"
|
||||
require_relative "helpers"
|
||||
|
||||
module Test
|
||||
Factory = ROM::Factory.configure { |config|
|
||||
config.rom = Test::DB::Helpers.rom
|
||||
}
|
||||
|
||||
module DB
|
||||
class FactoryHelper < Module
|
||||
ENTITIES_MODULE_NAME = :Entities
|
||||
|
||||
attr_reader :slice_name
|
||||
|
||||
def initialize(slice_name = nil)
|
||||
@slice_name = slice_name
|
||||
|
||||
factory = entity_namespace ? Test::Factory.struct_namespace(entity_namespace) : Factory
|
||||
|
||||
define_method(:factory) do
|
||||
factory
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def entity_namespace
|
||||
return @entity_namespace if instance_variable_defined?(:@entity_namespace)
|
||||
|
||||
slice = slice_name ? Hanami.app.slices[slice_name] : Hanami.app
|
||||
slice_namespace = slice.namespace
|
||||
|
||||
@entity_namespace =
|
||||
if slice_namespace.const_defined?(ENTITIES_MODULE_NAME)
|
||||
slice_namespace.const_get(ENTITIES_MODULE_NAME)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Dir[SPEC_ROOT.join("support/factories/**/*.rb")].each { require(_1) }
|
7
spec/support/factories/posts.rb
Normal file
7
spec/support/factories/posts.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
Test::Factory.define(:post) do |f|
|
||||
f.name "post_name"
|
||||
f.content "post_content"
|
||||
f.slug "post-slug"
|
||||
f.published_at Time.now
|
||||
f.syndication_sources {}
|
||||
end
|
20
spec/support/feature_loader.rb
Normal file
20
spec/support/feature_loader.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "dry/system"
|
||||
|
||||
RSpec.configure do |config|
|
||||
Dir[File.join(__dir__, "*.rb")].sort.each do |file|
|
||||
options = Dry::System::MagicCommentsParser.call(file)
|
||||
tag_name = options[:require_with_metadata]
|
||||
|
||||
next unless tag_name
|
||||
|
||||
tag_name = File.basename(file, File.extname(file)) if tag_name.eql?(true)
|
||||
|
||||
config.when_first_matching_example_defined(tag_name.to_sym) do
|
||||
require file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Dir[SPEC_ROOT.join("support", "**", "global_config.rb")].each { require _1 }
|
25
spec/syndication/mastodon_spec.rb
Normal file
25
spec/syndication/mastodon_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Adamantium::Syndication::Mastodon do
|
||||
subject { described_class.new }
|
||||
|
||||
describe "syndication to mastodon" do
|
||||
let(:post) {
|
||||
Adamantium::Entities::PostRequest.new(
|
||||
h: "h-type",
|
||||
action: nil,
|
||||
name: "My Post",
|
||||
content: "Content",
|
||||
slug: "my-post",
|
||||
category: ["ruby", "rspec"],
|
||||
published_at: Time.now,
|
||||
post_type: "post"
|
||||
)
|
||||
}
|
||||
|
||||
it "syndicates when it has a post" do
|
||||
response = subject.call(post: post)
|
||||
expect(response).to be_success
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user