diff --git a/app/commands/posts/create_checkin.rb b/app/commands/posts/create_checkin.rb new file mode 100644 index 0000000..5f63793 --- /dev/null +++ b/app/commands/posts/create_checkin.rb @@ -0,0 +1,40 @@ +require "dry/monads" + +module Adamantium + module Commands + module Posts + class CreateCheckin < Command + include Deps["repos.post_repo", + "post_utilities.slugify", + renderer: "renderers.markdown", + add_post_syndication_source: "commands.posts.add_syndication_source" + ] + + include Dry::Monads[:result] + + def call(post) + post_params = prepare_params(params: post) + created_post = post_repo.create(post_params) + + post_params[:syndication_sources].each do |url| + add_post_syndication_source.call(created_post.id, :swarm, url) + end + + # decorated_post = Decorators::Posts::Decorator.new(created_post) + + # send_webmentions.call(post_content: attrs[:content], post_url: decorated_post.permalink) + + Success(created_post) + end + + private + + def prepare_params(params:) + attrs = params.to_h + attrs[:content] = renderer.call(content: attrs[:content]) + attrs + end + end + end + end +end diff --git a/app/commands/posts/creation_resolver.rb b/app/commands/posts/creation_resolver.rb index 36a1de7..b236d0d 100644 --- a/app/commands/posts/creation_resolver.rb +++ b/app/commands/posts/creation_resolver.rb @@ -5,14 +5,18 @@ module Adamantium include Deps[ "validation.posts.post_contract", "validation.posts.bookmark_contract", + "validation.posts.checkin_contract", "commands.posts.create_entry", - "commands.posts.create_bookmark" + "commands.posts.create_bookmark", + "commands.posts.create_checkin" ] def call(entry_type:) case entry_type in Entities::BookmarkRequest {command: create_bookmark, validation: bookmark_contract} + in Entities::CheckinRequest + {command: create_checkin, validation: checkin_contract} else {command: create_entry, validation: post_contract} end diff --git a/app/entities/checkin_request.rb b/app/entities/checkin_request.rb new file mode 100644 index 0000000..979b94c --- /dev/null +++ b/app/entities/checkin_request.rb @@ -0,0 +1,17 @@ +module Adamantium + module Entities + class CheckinRequest < Dry::Struct + attribute :h, Types::Coercible::String + attribute :name, Types::Coercible::String.optional + attribute :content, Types::Coercible::String.optional + attribute :slug, Types::Coercible::String + attribute :url, Types::Coercible::String + attribute :category, Types::Array.of(Types::Coercible::String) + attribute :published_at, Types::Nominal::DateTime.optional + attribute :post_type, Types::Coercible::String + attribute :syndication_sources, Types::Array.of(Types::Coercible::String) + attribute :photos, Types::Array.of(Types::Hash) + attribute :location, Types::Coercible::String + end + end +end diff --git a/app/templates/shared/_post.html.slim b/app/templates/shared/_post.html.slim index d1bf5e6..3b0b5e4 100644 --- a/app/templates/shared/_post.html.slim +++ b/app/templates/shared/_post.html.slim @@ -2,7 +2,7 @@ div class="mb-8 h-entry" h3 class="text-xl font-semibold text-blue-600 mb-2" a class="u-url border-b-2 border-transparent hover:border-blue-600 hover:border-b-2" href="/post/#{post.slug}" = post.display_title - div class="e-content p-name text-base prose prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 text-gray-800 dark:text-gray-200" + div class="e-content p-name text-base prose prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 text-gray-800 dark:text-gray-200 prose-a:dark:text-gray-100" == post.excerpt -if post.location img class="rounded" src=post.small_map diff --git a/app/validation/posts/checkin_contract.rb b/app/validation/posts/checkin_contract.rb new file mode 100644 index 0000000..e6ecaa2 --- /dev/null +++ b/app/validation/posts/checkin_contract.rb @@ -0,0 +1,19 @@ +module Adamantium + module Validation + module Posts + class CheckinContract < Dry::Validation::Contract + params do + required(:name).maybe(:string) + required(:content).filled(:string) + required(:category).array(:string) + required(:published_at).maybe(:time) + required(:slug).filled(:string) + required(:post_type).value(included_in?: %w[checkin]) + required(:syndication_sources).array(:string) + required(:photos).array(:hash) + required(:location).maybe(:string) + end + end + end + end +end diff --git a/lib/adamantium/micropub_request_parser.rb b/lib/adamantium/micropub_request_parser.rb index 3120fcf..7fc5613 100644 --- a/lib/adamantium/micropub_request_parser.rb +++ b/lib/adamantium/micropub_request_parser.rb @@ -5,12 +5,18 @@ module Adamantium cont_type = content_type(params) req_type = request_type(params) - req_params = parse_params(req_type, cont_type, params) if cont_type == :bookmark + req_params = parse_post_params(req_type, cont_type, params) return Entities::BookmarkRequest.new(req_params) end + if cont_type == :checkin + checkin_params = parse_checkin_params(params) + return Entities::CheckinRequest.new(checkin_params) + end + + req_params = parse_post_params(req_type, cont_type, params) Entities::PostRequest.new(req_params) end @@ -18,6 +24,7 @@ module Adamantium def content_type(params) return :bookmark if params[:"bookmark-of"] + return :checkin if params.dig(:properties, :checkin) :post end @@ -33,7 +40,7 @@ module Adamantium nil end - def parse_params(req_type, post_type, params) + def parse_post_params(req_type, post_type, params) new_params = {} new_params[:h] = "entry" new_params[:post_type] = post_type @@ -56,7 +63,13 @@ module Adamantium new_params[:slug] = params[:slug] || params["mp-slug"] new_params[:published_at] = (params[:"post-status"] == "draft") ? nil : publish_time new_params[:category] = params[:category] || [] - new_params[:photos] = params[:photo] || [] + new_params[:photos] = if params[:photo].is_a?(String) + {value: params[:photo], alt: ""} + elsif params[:photo].nil? + [] + else + params[:photo] + end new_params[:location] = params[:location] content = if params[:content] @@ -74,5 +87,24 @@ module Adamantium new_params end + + def parse_checkin_params(params) + new_params = {} + + checkin = params.dig(:properties, :checkin).first + new_params[:h] = "entry" + new_params[:syndication_sources] = params.dig(:properties, :syndication) + new_params[:name] = checkin.dig(:properties, :name).first + new_params[:content] = params.dig(:properties, :content)&.first + new_params[:url] = checkin.dig(:properties, :url)&.first + new_params[:slug] = SecureRandom.uuid + new_params[:category] = params.dig(:properties, :category) + new_params[:published_at] = params.dig(:properties, :published)&.first + new_params[:post_type] = :checkin + location = params.dig(:properties, :location).first[:properties] + new_params[:photos] = [] + new_params[:location] = "geo:#{location.dig(:latitude).first},#{location.dig(:longitude).first};u=0" + new_params + end end end diff --git a/spec/adamantium/micropub_request_parser_spec.rb b/spec/adamantium/micropub_request_parser_spec.rb index 28ad74a..8d1e112 100644 --- a/spec/adamantium/micropub_request_parser_spec.rb +++ b/spec/adamantium/micropub_request_parser_spec.rb @@ -42,4 +42,106 @@ RSpec.describe Adamantium::MicropubRequestParser do end end end + + context "checkin request" do + let(:params) { + json = '{ + "type": [ + "h-entry" + ], + "properties": { + "published": [ + "2023-02-25T10:24:30+11:00" + ], + "syndication": [ + "https://www.swarmapp.com/user/1390949/checkin/63f9472ed36fa977ac188903" + ], + "content": [ + "Coffee time!!" + ], + "category": [ + "check-in" + ], + "checkin": [ + { + "type": [ + "h-card" + ], + "properties": { + "name": [ + "St Rose" + ], + "url": [ + "https://foursquare.com/v/527da837498e9d7fee64bb75", + "http://www.strose.com.au" + ], + "tel": [ + "(03) 9331 4488" + ], + "latitude": [ + -37.75627 + ], + "longitude": [ + 144.91555 + ], + "street-address": [ + "19 Rose St" + ], + "locality": [ + "Essendon" + ], + "region": [ + "VIC" + ], + "country-name": [ + "Australia" + ], + "postal-code": [ + "3040" + ] + }, + "value": "https://foursquare.com/v/527da837498e9d7fee64bb75" + } + ], + "location": [ + { + "type": [ + "h-adr" + ], + "properties": { + "latitude": [ + -37.75627 + ], + "longitude": [ + 144.91555 + ], + "street-address": [ + "19 Rose St" + ], + "locality": [ + "Essendon" + ], + "region": [ + "VIC" + ], + "country-name": [ + "Australia" + ], + "postal-code": [ + "3040" + ] + } + } + ] + } +}' + JSON.parse(json, symbolize_names: true) + } + it "parses the request" do + Timecop.freeze do + result = subject.call(params: params) + expect(result).to be_a Adamantium::Entities::CheckinRequest + end + end + end end diff --git a/spec/requests/create_post_spec.rb b/spec/requests/create_post_spec.rb index 8093898..57243e4 100644 --- a/spec/requests/create_post_spec.rb +++ b/spec/requests/create_post_spec.rb @@ -58,4 +58,21 @@ RSpec.describe "Post creation", :db, :requests do expect(post_repo.bookmark_listing.count).to eq 1 end end + + context "auth" do + it "does not allow multiple auth methods" do + params = { + access_token: "foo" + } + + headers = { + HTTP_AUTHORIZATION: "Bearer foo", + CONTENT_TYPE: "application/json" + } + + post "/micropub", params.to_json, headers + + expect(last_response.status).to eq 400 + end + end end