Save syndication sources to posts

This commit is contained in:
2023-01-29 16:54:34 +11:00
parent 37932ea4ad
commit 2d371812f5
17 changed files with 231 additions and 10 deletions

View File

@@ -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}"

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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:&nbsp;
- post.syndicated_to.each do |loc|
a href=loc[:url]
== render loc[:location]

View 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

View File

@@ -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

View File

@@ -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));

View 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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View 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) }

View 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

View 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 }

View 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