Browse Source

fix(webmention): Handle incoming + sending.

jackyalcine 8 months ago
parent
commit
7454b6d541
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D

+ 3
- 0
config/config.exs View File

@@ -82,4 +82,7 @@ config :liquid, error_mode: :strict
82 82
 
83 83
 config :mnesia, dir: 'priv/mnesia/#{Mix.env()}/#{node()}'
84 84
 
85
+config :indieweb,
86
+  :webmention_url_adapter, Koype.Webmention.URIAdapter
87
+
85 88
 import_config "#{Mix.env()}.exs"

+ 1
- 1
config/test.exs View File

@@ -9,7 +9,7 @@ config :koype, Koype.Web.Endpoint,
9 9
   server: true,
10 10
   http: [port: {:system, :string, "TEST_PORT", 5001}]
11 11
 
12
-config :logger, level: :warn
12
+config :logger, level: :error
13 13
 
14 14
 config :exvcr,
15 15
   vcr_cassette_library_dir: "test/fixtures/vcr_cassettes",

+ 1
- 1
lib/feed/atom.ex View File

@@ -48,7 +48,7 @@ defmodule Koype.Feed.Atom do
48 48
     if item.author != Koype.host() do
49 49
       with(
50 50
         {:ok, webmention_json} <- Koype.Repo.Webmention.Json.find(item),
51
-        {:ok, entry} <- IndieWeb.Webmention.resolve_target(item.target)
51
+        {:ok, entry} <- IndieWeb.Webmention.resolve_target_from_url(item.target)
52 52
       ) do
53 53
         author_name =
54 54
           case IndieWeb.HCard.resolve(item.author) do

+ 15
- 14
lib/feed/json_feed.ex View File

@@ -94,24 +94,11 @@ defmodule Koype.Feed.Json do
94 94
     end
95 95
   end
96 96
 
97
-  defp do_extract_category_names(categories)
98
-  defp do_extract_category_names([]), do: []
99
-
100
-  defp do_extract_category_names(categories) when is_list(categories) do
101
-    Enum.map(categories, fn
102
-      nil -> nil
103
-      %{"name" => name} -> name
104
-      %{name: name} -> name
105
-      _ -> nil
106
-    end)
107
-    |> Enum.reject(&is_nil/1)
108
-  end
109
-
110 97
   def do_wrap_item_into_json(%Koype.Repo.Webmention{} = item) do
111 98
     if item.author != Koype.host() do
112 99
       with(
113 100
         {:ok, webmention_json} <- Koype.Repo.Webmention.Json.find(item),
114
-        {:ok, entry} <- IndieWeb.Webmention.resolve_target(item.target)
101
+        {:ok, entry} <- IndieWeb.Webmention.resolve_target_from_url(item.target)
115 102
       ) do
116 103
         author_name =
117 104
           case IndieWeb.HCard.resolve(item.author) do
@@ -155,4 +142,18 @@ defmodule Koype.Feed.Json do
155 142
       end
156 143
     end
157 144
   end
145
+
146
+
147
+  defp do_extract_category_names(categories)
148
+  defp do_extract_category_names([]), do: []
149
+
150
+  defp do_extract_category_names(categories) when is_list(categories) do
151
+    Enum.map(categories, fn
152
+      nil -> nil
153
+      %{"name" => name} -> name
154
+      %{name: name} -> name
155
+      _ -> nil
156
+    end)
157
+    |> Enum.reject(&is_nil/1)
158
+  end
158 159
 end

+ 4
- 4
lib/http.ex View File

@@ -40,8 +40,8 @@ defmodule Koype.Http do
40 40
   defmodule Error do
41 41
     @moduledoc "Represents a HTTP error."
42 42
     @enforce_keys [:reason]
43
-    defstruct ~w(reason raw)a
44
-    @type t :: %Error{reason: any(), raw: any()}
43
+    defstruct ~w(reason raw url)a
44
+    @type t :: %Error{reason: any(), raw: any(), url: binary()}
45 45
   end
46 46
 
47 47
   for method <- ~w(get post put patch delete head options)a do
@@ -108,7 +108,7 @@ defmodule Koype.Http do
108 108
           {:ok, %Koype.Http.Response{code: code, body: body, headers: resp.headers.hdrs, raw: resp}}
109 109
 
110 110
         %HTTPotion.ErrorResponse{message: reason} = resp ->
111
-          {:error, %Koype.Http.Error{reason: reason, raw: resp}}
111
+          {:error, %Koype.Http.Error{reason: reason, raw: resp, url: final_args[:url]}}
112 112
       end
113 113
     rescue
114 114
       err ->
@@ -142,7 +142,7 @@ defmodule Koype.Http do
142 142
           {:ok, %Koype.Http.Response{code: code, body: body, headers: resp.headers |> Map.new(), raw: resp}}
143 143
 
144 144
         {:error, %HTTPoison.Error{reason: reason} = resp} ->
145
-          {:error, %Koype.Http.Error{reason: reason, raw: resp}}
145
+          {:error, %Koype.Http.Error{reason: reason, raw: resp, url: final_args[:url]}}
146 146
       end
147 147
     rescue
148 148
       err ->

+ 0
- 37
lib/indieweb.ex View File

@@ -1,37 +0,0 @@
1
-# Koype: a IndieWeb-focused, single-tenant website engine for people.
2
-# 
3
-# Copyright © 2019 Jacky Alciné <jacky.is@black.af>
4
-# 
5
-# This file belongs to the Koype project.
6
-# 
7
-# This program is free software: you can redistribute it and/or modify
8
-# it under the terms of the GNU Affero General Public License as published by
9
-# the Free Software Foundation, either version 3 of the License, or
10
-# (at your option) any later version.
11
-# 
12
-# This program is distributed in the hope that it will be useful,
13
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
-# GNU Affero General Public License for more details.
16
-# 
17
-# You should have received a copy of the GNU Affero General Public License
18
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
-defmodule IndieWeb do
20
-  def links() do
21
-    %{
22
-      authorization_endpoint: IndieWeb.Auth.auth_endpoint(),
23
-      me: IndieWeb.RelMe.all() |> Enum.map(& &1.uri),
24
-      media_endpoint: IndieWeb.Micropub.media_endpoint(),
25
-      micropub: IndieWeb.Micropub.endpoint(),
26
-      microsub: IndieWeb.Microsub.endpoint(),
27
-      self: Koype.host(),
28
-      token_endpoint: IndieWeb.Auth.token_endpoint(),
29
-      subscribe: IndieWeb.Microsub.subscription_endpoint()
30
-    }
31
-    |> Enum.reject(fn
32
-      {_, v} when is_nil(v) or length(v) == 0 -> true
33
-      _ -> false
34
-    end)
35
-    |> Map.new()
36
-  end
37
-end

+ 1
- 1
lib/indieweb/micropub/entry.ex View File

@@ -39,7 +39,7 @@ defmodule IndieWeb.Micropub.Entry do
39 39
             "Found the following links for #{entry_uri} to dispatch Webmentions to: " <> Enum.join(links, "; ")
40 40
           )
41 41
 
42
-          Enum.each(links, fn link -> IndieWeb.Webmention.send(source: entry_uri, target: link) end)
42
+          Enum.each(links, fn link -> Koype.Webmention.send(target: link, source: model) end)
43 43
         else
44 44
           Logger.info("No Webmention-friendly links found for #{entry_uri}; not sending any Webmentions.")
45 45
           :ok

+ 19
- 1
lib/koype.ex View File

@@ -20,7 +20,6 @@ defmodule Koype do
20 20
   @moduledoc """
21 21
   Provides some global helper methods for the project.
22 22
   """
23
-  require Logger
24 23
 
25 24
   @spec host() :: binary()
26 25
   def host() do
@@ -37,4 +36,23 @@ defmodule Koype do
37 36
   def version() do
38 37
     "0.0.5"
39 38
   end
39
+
40
+  @spec links() :: map()
41
+  def links() do
42
+    %{
43
+      authorization_endpoint: IndieWeb.Auth.auth_endpoint(),
44
+      me: IndieWeb.RelMe.all() |> Enum.map(& &1.uri),
45
+      media_endpoint: IndieWeb.Micropub.media_endpoint(),
46
+      micropub: IndieWeb.Micropub.endpoint(),
47
+      microsub: IndieWeb.Microsub.endpoint(),
48
+      self: Koype.host(),
49
+      token_endpoint: IndieWeb.Auth.token_endpoint(),
50
+      subscribe: IndieWeb.Microsub.subscription_endpoint()
51
+    }
52
+    |> Enum.reject(fn
53
+      {_, v} when is_nil(v) or length(v) == 0 -> true
54
+      _ -> false
55
+    end)
56
+    |> Map.new()
57
+  end
40 58
 end

+ 1
- 1
lib/repo.ex View File

@@ -67,7 +67,7 @@ defmodule Koype.Repo do
67 67
     end
68 68
   end
69 69
 
70
-  def find_by_columns(_schema, value, _columns) when is_nil(value), do: {:error, :not_found}
70
+  def find_by_columns(_schema, value, _columns) when is_nil(value), do: {:error, :model_not_found}
71 71
 
72 72
   def get_uri_for_record(%{slug: slug}, router_method) when is_binary(slug) do
73 73
     URI.merge(URI.parse(Koype.host()), router_method.(Koype.Web.Endpoint, :view, slug)) |> URI.to_string()

+ 31
- 123
lib/webmention.ex View File

@@ -31,10 +31,10 @@ defmodule Koype.Webmention do
31 31
     def to_source_url(_), do: nil
32 32
 
33 33
     @impl true
34
-    def from_target_uri(target_uri) do
34
+    def from_source_url(target_uri) do
35 35
       Enum.reduce_while(
36 36
         [&do_resolve_homepage_for_target/1, &do_resolve_model_for_target/1],
37
-        {:error, :not_found},
37
+        nil,
38 38
         fn approach, acc ->
39 39
           case approach.(target_uri) do
40 40
             {:error, _} -> {:cont, acc}
@@ -48,7 +48,7 @@ defmodule Koype.Webmention do
48 48
       current_host = Koype.host()
49 49
 
50 50
       cond do
51
-        "/" == target_uri ->
51
+        "/" == target_uri || current_host == target_uri ->
52 52
           Logger.info("#{target_uri} matches our homepage.")
53 53
           {:ok, :homepage}
54 54
 
@@ -70,8 +70,8 @@ defmodule Koype.Webmention do
70 70
           Logger.info("Using #{module} to look up model from the URI #{target_uri}.")
71 71
 
72 72
           case module.resolve_from_uri(target_uri) do
73
+            {:error, _} -> {:cont, acc}
73 74
             {:ok, model} -> {:halt, {:ok, model}}
74
-            _ -> {:cont, acc}
75 75
           end
76 76
         end
77 77
       )
@@ -96,17 +96,17 @@ defmodule Koype.Webmention do
96 96
     end
97 97
   end
98 98
 
99
-  def direct_send!(source: source, target: target, endpoint: endpoint) do
100
-    case IndieWeb.Webmention.direct_send(endpoint, target, source) do
101
-      {:ok, _} = resp ->
102
-        Logger.debug("Webmention for #{inspect(source)} to #{target} via #{endpoint}!")
103
-        resp
99
+  # def direct_send!(source: source, target: target, endpoint: endpoint) do
100
+  #   case IndieWeb.Webmention.direct_send(endpoint, target, source) do
101
+  #     {:ok, _} = resp ->
102
+  #       Logger.debug("Webmention for #{inspect(source)} to #{target} via #{endpoint}!")
103
+  #       resp
104 104
 
105
-      error ->
106
-        Logger.info("Failed to send Webmention to #{target} from #{inspect(source)} via #{endpoint}: #{inspect(error)}")
107
-        error
108
-    end
109
-  end
105
+  #     error ->
106
+  #       Logger.info("Failed to send Webmention to #{target} from #{inspect(source)} via #{endpoint}: #{inspect(error)}")
107
+  #       error
108
+  #   end
109
+  # end
110 110
 
111 111
   def receive(args) do
112 112
     Koype.Job.create(Koype.Job.Webmention.Receive, args)
@@ -114,8 +114,13 @@ defmodule Koype.Webmention do
114 114
 
115 115
   def receive!(args) do
116 116
     case IndieWeb.Webmention.receive(args) do
117
-      {:ok, _} ->
118
-        do_build_webmention(args)
117
+      {:ok, from: source, target: {:ok, model}} ->
118
+        target_uri = case URI.parse(args[:target]) do
119
+          %URI{path: path} when is_binary(path) -> path
120
+          _ -> "/"
121
+        end
122
+
123
+        do_build_webmention(source: source, target: model, target_uri: target_uri)
119 124
 
120 125
       {:error, _} = error ->
121 126
         error
@@ -138,21 +143,14 @@ defmodule Koype.Webmention do
138 143
 
139 144
   defp do_deletion_of_webmention(args) do
140 145
     Koype.Repo.Webmention
141
-    |> Koype.Repo.all(source: args[:source], target: URI.parse(args[:target]).path)
146
+    |> Koype.Repo.all(source: args[:source], target: args[:target_uri])
142 147
     |> Enum.map(&Koype.Repo.delete(&1))
143 148
 
144 149
     Logger.info("Dropped all relating Webmentions from the database.")
145 150
     {:ok, :gone}
146 151
   end
147 152
 
148
-  defp do_format_target_as_path(url) when is_binary(url) do
149
-    case URI.parse(url).path do
150
-      "" -> "/"
151
-      path -> path
152
-    end
153
-  end
154
-
155
-  defp do_upsertion_of_webmention(source: source, target: target) do
153
+  defp do_upsertion_of_webmention(source: source, target: target, target_uri: target_uri) do
156 154
     case IndieWeb.MF2.Remote.fetch(source) do
157 155
       {:ok, mf2} ->
158 156
         entry_mf2 = IndieWeb.MF2.get_format(mf2, "entry")
@@ -163,20 +161,15 @@ defmodule Koype.Webmention do
163 161
           "Detected #{source} to have #{inspect(types)} types and be a #{type} at its base."
164 162
         end)
165 163
 
166
-        Logger.info("Saving Webmention from #{source} to #{do_format_target_as_path(target)} // #{target}.")
164
+        Logger.info("Saving Webmention from #{source} to #{target_uri} // #{target}.")
165
+
166
+        case Koype.Repo.Webmention.upsert(source: source,
167
+                                     target: target_uri,
168
+                                     type: Atom.to_string(type),
169
+                                     mf2: entry_mf2) do
170
+          {:ok, _} = result ->
171
+            result
167 172
 
168
-        with(
169
-          {:ok, _} <- IndieWeb.Webmention.resolve_target_from_url(target),
170
-          {:ok, webmention_model} <-
171
-            Koype.Repo.Webmention.upsert(
172
-              source: source,
173
-              target: do_format_target_as_path(target),
174
-              type: Atom.to_string(type),
175
-              mf2: entry_mf2
176
-            )
177
-        ) do
178
-          {:ok, webmention_model}
179
-        else
180 173
           {:error, error} = err ->
181 174
             Logger.error("Failed to create Webmention from #{source} to #{target}: #{inspect(error)}.")
182 175
             IndieWeb.MF2.Remote.flush(source)
@@ -189,89 +182,4 @@ defmodule Koype.Webmention do
189 182
         err
190 183
     end
191 184
   end
192
-
193
-  defp do_validate_scheme(uri)
194
-  defp do_validate_scheme(uri) when is_binary(uri), do: do_validate_scheme(URI.parse(uri))
195
-  defp do_validate_scheme(%URI{scheme: "http"}), do: :ok
196
-  defp do_validate_scheme(%URI{scheme: "https"}), do: :ok
197
-  defp do_validate_scheme(_), do: {:error, :scheme_not_supported_for_webmentions}
198
-
199
-  defp do_fetch_endpoint_from_headers(site, method)
200
-  defp do_fetch_endpoint_from_headers(site, :head), do: do_extract_headers_from_resp(Koype.Http.head(site), site)
201
-  defp do_fetch_endpoint_from_headers(site, :get), do: do_extract_headers_from_resp(Koype.Http.get(site), site)
202
-
203
-  defp do_extract_headers_from_resp({:ok, %Koype.Http.Response{headers: headers, code: code}}, site)
204
-       when code >= 200 and code < 300 do
205
-    link = Map.take(headers, ["link", "Link"]) |> Map.values() |> List.first()
206
-
207
-    if is_nil(link) do
208
-      Logger.info("No 'link' header found for #{site}.")
209
-      {:error, :no_info_from_headers}
210
-    else
211
-      webmention_link_rel =
212
-        link
213
-        |> String.split(",")
214
-        |> Enum.map(fn v -> String.split(v, ";") |> Enum.map(fn f -> String.trim(f) end) end)
215
-        |> Enum.filter(fn [_, rel | _] -> String.contains?(rel, "webmention") end)
216
-
217
-      if Enum.empty?(webmention_link_rel) do
218
-        {:error, :no_info_from_headers}
219
-      else
220
-        url =
221
-          webmention_link_rel
222
-          |> List.first()
223
-          |> List.first()
224
-          |> String.slice(1..-2)
225
-
226
-        Logger.info("Found #{url} as Webmention endpoint for #{site}.")
227
-        {:ok, url}
228
-      end
229
-    end
230
-  end
231
-
232
-  defp do_extract_headers_from_resp(_, site) do
233
-    Logger.info("Failed to fetch page to extract headers from for #{site}.")
234
-    {:error, :no_info_from_headers}
235
-  end
236
-
237
-  defp do_fetch_endpoint_from_relme(site) do
238
-    with(
239
-      {:ok, mf2} <- Remote.fetch(site),
240
-      rels <- Map.get(mf2, "rels", %{}),
241
-      webmentions <- Map.get(rels, "webmention", [site])
242
-    ) do
243
-      case List.first(webmentions) do
244
-        nil -> {:error, :no_relme_data_found}
245
-        uri -> {:ok, uri}
246
-      end
247
-    else
248
-      {:error, _} = err -> err
249
-    end
250
-  end
251
-
252
-  # TODO: Send path for callback information.
253
-  # TODO: Save information about sending this Webmention.
254
-  defp dispatch_http_request(endpoint, args) do
255
-    body = %{source: args[:source], target: args[:target]}
256
-
257
-    case Koype.Http.post(
258
-           endpoint,
259
-           body: URI.encode_query(body),
260
-           headers: %{
261
-             "Content-Type" => "application/x-www-form-urlencoded"
262
-           }
263
-         ) do
264
-      {:ok, %Koype.Http.Response{code: code, body: body, headers: headers}} when code >= 200 and code < 300 ->
265
-        Logger.debug(fn -> "Webmention sent to #{endpoint} for #{inspect(args)} successfully." end)
266
-        {:ok, body: body, headers: headers}
267
-
268
-      {:ok, %Koype.Http.Response{body: body, code: code}} when code >= 400 and code < 599 ->
269
-        Logger.info(fn -> "Server failed to accept webmention sent to #{endpoint} for #{inspect(args)}: #{body}" end)
270
-        {:error, reason: :not_ok, raw: body}
271
-
272
-      {:error, issue} ->
273
-        Logger.warn("Failed to send webmention to #{endpoint} for #{inspect(args)}: #{inspect(issue)}")
274
-        {:error, reason: :unknown, raw: issue}
275
-    end
276
-  end
277 185
 end

+ 0
- 16
test/support/adapters/webmention.ex View File

@@ -1,16 +0,0 @@
1
-defmodule Koype.Test.WebmentionUrlAdapter do
2
-  @behaviour IndieWeb.Webmention.URIAdapter
3
-
4
-  def from_source_url(target_url)
5
-
6
-  def from_source_url(url) do
7
-    Koype.Repo.Entry.resolve_from_uri(url)
8
-  end
9
-
10
-  def to_source_url(target_url)
11
-  def to_source_url(:fake_source), do: URI.parse("https://source.indieweb/fake")
12
-  def to_source_url(%Koype.Repo.Entry{} = entry), do: URI.parse(Koype.Repo.Entry.get_uri(entry))
13
-  def to_source_url(_), do: nil
14
-end
15
-
16
-Application.put_env(:indieweb, :webmention_url_adapter, Koype.Test.WebmentionUrlAdapter, persistent: true)

+ 0
- 14
test/unit/http_test.exs View File

@@ -82,18 +82,4 @@ defmodule Koype.HttpTest do
82 82
       end
83 83
     end
84 84
   end
85
-
86
-  describe ".make_absolute_uri/2" do
87
-    test "resolves relative path" do
88
-      assert @url <> "/foo.php" == Subject.make_absolute_uri("/foo.php", @url)
89
-    end
90
-
91
-    test "resolves URI with leading dots" do
92
-      assert @url <> "/foo.php" == Subject.make_absolute_uri("../foo.php", @url <> "/cookie")
93
-    end
94
-
95
-    test "returns absolute path un-touched" do
96
-      assert @url == Subject.make_absolute_uri(@url, @url)
97
-    end
98
-  end
99 85
 end

+ 0
- 111
test/unit/indieweb/hcard_test.exs View File

@@ -1,111 +0,0 @@
1
-defmodule IndieWeb.HCardTest do
2
-  use Koype.Test.BaseCase
3
-  use ExVCR.Mock
4
-  alias IndieWeb.HCard, as: Subject
5
-
6
-  @url Faker.Internet.url()
7
-  @hcard %{
8
-    "url" => @url,
9
-    "uid" => @url,
10
-    "photo" => Faker.Avatar.image_url(),
11
-    "note" => Faker.Lorem.sentence(),
12
-    "name" => Faker.Name.name()
13
-  }
14
-
15
-  defp do_stringify_map(map), do: Jason.encode!(map) |> Jason.decode!(keys: :strings, strings: :copy)
16
-
17
-  describe ".fetch_representative/1" do
18
-    test "finds top-level h-card contains a u-uid and u-url" do
19
-      html = """
20
-      <html>
21
-      <body>
22
-      <div class="h-card">
23
-      <img class="u-photo" src="#{@hcard["photo"]}" />
24
-      <a href="#{@url}" class="u-url u-uid">
25
-      <span class="p-name">#{@hcard["name"]}</span>
26
-      </a>
27
-      <p class="p-note">#{@hcard["note"]}</p>
28
-      </div>
29
-      </body>
30
-      </html>
31
-      """
32
-
33
-      mf2 = Microformats2.parse(html, @url) |> do_stringify_map
34
-      assert {:ok, @hcard} = Subject.fetch_representative(mf2, @url)
35
-    end
36
-
37
-    test "finds h-card where url matches a rel=me path" do
38
-      html = """
39
-      <html>
40
-      <link rel="me" href="#{@hcard["url"]}/relme"
41
-      <body>
42
-      <div class="h-card">
43
-      <img class="u-photo" src="#{@hcard["photo"]}" />
44
-      <a href="#{@url}/relme" class="u-url u-uid">
45
-      <span class="p-name">#{@hcard["name"]}</span>
46
-      </a>
47
-      <p class="p-note">#{@hcard["note"]}</p>
48
-      </div>
49
-      </body>
50
-      </html>
51
-      """
52
-
53
-      mf2 = Microformats2.parse(html, @url) |> do_stringify_map
54
-
55
-      expected_hcard =
56
-        Map.merge(@hcard, %{
57
-          "url" => @hcard["url"] <> "/relme",
58
-          "uid" => @hcard["url"] <> "/relme"
59
-        })
60
-
61
-      assert {:ok, ^expected_hcard} = Subject.fetch_representative(mf2, @url)
62
-    end
63
-
64
-    test "finds lone h-card on page" do
65
-      html = """
66
-      <html>
67
-      <body>
68
-      <div class="h-card">
69
-      <img class="u-photo" src="#{@hcard["photo"]}" />
70
-      <a href="#{@url}/lone" class="u-url u-uid">
71
-      <span class="p-name">#{@hcard["name"]}</span>
72
-      </a>
73
-      <p class="p-note">#{@hcard["note"]}</p>
74
-      </div>
75
-      </body>
76
-      </html>
77
-      """
78
-
79
-      mf2 = Microformats2.parse(html, @url) |> do_stringify_map
80
-
81
-      expected_hcard =
82
-        Map.merge(@hcard, %{
83
-          "url" => @hcard["url"] <> "/lone",
84
-          "uid" => @hcard["url"] <> "/lone"
85
-        })
86
-
87
-      assert {:ok, ^expected_hcard} = Subject.fetch_representative(mf2, @url)
88
-    end
89
-  end
90
-
91
-  describe ".resolve/1" do
92
-    test "successfully resolves a h-card from the provided URI" do
93
-      use_cassette "finds_hcard_from_homepage" do
94
-        assert {:ok, %{"name" => "Aaron Parecki"}} = Subject.resolve("https://aaronparecki.com")
95
-      end
96
-    end
97
-
98
-    test "successfully resolves a h-card from authorship" do
99
-      use_cassette "finds_hcard_from_authorship" do
100
-        assert {:ok, %{"name" => "Aaron Parecki"}} =
101
-                 Subject.resolve("https://aaronparecki.com/2018/12/17/7/blocking-domains")
102
-      end
103
-    end
104
-
105
-    test "resolves generic h-card from non-MF2 site" do
106
-      use_cassette "generate_hcard_from_uri" do
107
-        assert {:ok, %{"name" => "firefox.com"}} = Subject.resolve("http://firefox.com")
108
-      end
109
-    end
110
-  end
111
-end

+ 47
- 83
test/unit/webmention_test.exs View File

@@ -9,46 +9,49 @@ defmodule Koype.WebmentionTest do
9 9
 
10 10
   doctest Subject
11 11
 
12
-  setup do
13
-    Application.put_env(:indieweb, :webmention_url_adapter, Koype.Test.WebmentionUrlAdapter, persistent: true)
14
-  end
15
-
16 12
   describe ".receive!/1" do
17 13
     test "stores new for model" do
18
-      mf2 = %{
19
-        "rels" => %{},
20
-        "items" => [
21
-          %{
22
-            "type" => ["h-entry"],
23
-            "properties" => %{
24
-              "author" => [
25
-                %{
26
-                  "properties" => %{
27
-                    "name" => [Faker.Name.name()],
28
-                    "photo" => [Faker.Avatar.image_url()],
29
-                    "url" => [Faker.Internet.url()],
30
-                    "note" => [Faker.Lorem.sentence()]
31
-                  },
32
-                  "type" => ["h-card"]
33
-                }
34
-              ],
35
-              "like-of" => [Faker.Internet.url()]
36
-            }
37
-          }
38
-        ]
39
-      }
40
-
41 14
       model = insert(:entry)
42 15
       source_uri = Faker.Internet.url()
43 16
       target_uri = Koype.Repo.Entry.get_uri(model)
17
+      html = """
18
+      <html>
19
+      <body>
20
+      <div class="h-entry">
21
+      <a class="p-author h-card" href="/"><span class="p-name">#{Faker.Name.name()}</span></a>
22
+      <a class="u-like-of" href="#{target_uri}">Wow</a>
23
+      </div>
24
+      </body>
25
+      </html>
26
+      """
27
+
28
+      use_cassette :stub, uri: source_uri, body: html do
29
+        assert {:ok, webmention} = Subject.receive!(source: source_uri, target: target_uri)
30
+        assert webmention.source == source_uri
31
+        assert webmention.target == URI.parse(target_uri).path
32
+        assert webmention.type == "like"
33
+      end
34
+    end
44 35
 
45
-      use_cassette :stub, uri: source_uri, code: 200 do
46
-        with_mock(IndieWeb.MF2.Remote, [], fetch: fn _ -> {:ok, mf2} end, flush: fn _ -> :ok end) do
47
-          assert {:ok, webmention} = Subject.receive!(source: source_uri, target: target_uri)
48
-          assert webmention.source == source_uri
49
-          assert webmention.target == URI.parse(target_uri).path
50
-          assert webmention.type == "like"
51
-        end
36
+    test "stores new for homepage mention" do
37
+      source_uri = Faker.Internet.url()
38
+      target_uri = Koype.host() <> "/"
39
+      html = """
40
+      <html>
41
+      <body>
42
+      <div class="h-entry">
43
+      <a class="p-author h-card" href="/"><span class="p-name">#{Faker.Name.name()}</span></a>
44
+      <a class="u-like-of" href="#{target_uri}">Wow</a>
45
+      </div>
46
+      </body>
47
+      </html>
48
+      """
49
+
50
+      use_cassette :stub, uri: source_uri, body: html do
51
+        assert {:ok, webmention} = Subject.receive!(source: source_uri, target: target_uri)
52
+        assert webmention.source == source_uri
53
+        assert webmention.target == "/"
54
+        assert webmention.type == "like"
52 55
       end
53 56
     end
54 57
 
@@ -89,43 +92,6 @@ defmodule Koype.WebmentionTest do
89 92
       end
90 93
     end
91 94
 
92
-    test "stores new for homepage mention" do
93
-      mf2 = %{
94
-        "rels" => %{},
95
-        "items" => [
96
-          %{
97
-            "type" => ["h-entry"],
98
-            "properties" => %{
99
-              "author" => [
100
-                %{
101
-                  "properties" => %{
102
-                    "name" => [Faker.Name.name()],
103
-                    "photo" => [Faker.Avatar.image_url()],
104
-                    "url" => [Faker.Internet.url()],
105
-                    "note" => [Faker.Lorem.sentence()]
106
-                  },
107
-                  "type" => ["h-card"]
108
-                }
109
-              ],
110
-              "like-of" => [Faker.Internet.url()]
111
-            }
112
-          }
113
-        ]
114
-      }
115
-
116
-      source_uri = Faker.Internet.url()
117
-      target_uri = Koype.host() <> "/"
118
-
119
-      use_cassette :stub, uri: source_uri, code: 200 do
120
-        with_mock(IndieWeb.MF2.Remote, [], fetch: fn _ -> {:ok, mf2} end, flush: fn _ -> :ok end) do
121
-          assert {:ok, webmention} = Subject.receive!(source: source_uri, target: target_uri)
122
-          assert webmention.source == source_uri
123
-          assert webmention.target == "/"
124
-          assert webmention.type == "like"
125
-        end
126
-      end
127
-    end
128
-
129 95
     @tag skip: true
130 96
     test "updates existing - refreshes content" do
131 97
       mf2 = %{
@@ -220,13 +186,11 @@ defmodule Koype.WebmentionTest do
220 186
       source_uri = Faker.Internet.url()
221 187
       target_uri = Koype.Repo.Entry.get_uri(model)
222 188
 
223
-      use_cassette :stub, url: source_uri, status_code: 200 do
224
-        with_mocks([
225
-          {IndieWeb.MF2.Remote, [], fetch: fn _ -> {:ok, mf2} end, flush: fn _ -> :ok end},
226
-          {Koype.Storage.Json, [], persist: fn _, _ -> {:error, :test_error} end}
227
-        ]) do
228
-          assert {:error, :test_error} = Subject.receive!(source: source_uri, target: target_uri)
229
-        end
189
+      with_mocks([
190
+        {IndieWeb.MF2.Remote, [], fetch: fn _ -> {:ok, mf2} end, flush: fn _ -> :ok end},
191
+        {Koype.Storage.Json, [], persist: fn _, _ -> {:error, :test_error} end}
192
+      ]) do
193
+        assert {:error, :test_error} = Subject.receive!(source: source_uri, target: target_uri)
230 194
       end
231 195
     end
232 196
 
@@ -249,11 +213,11 @@ defmodule Koype.WebmentionTest do
249 213
       use_cassette :stub, url: source_uri, status_code: 200 do
250 214
         with_mocks([
251 215
           {IndieWeb.MF2.Remote, [],
252
-           fetch: fn
253
-             ^source_uri -> {:error, :test_error}
254
-             _ -> {:ok, mf2}
255
-           end,
256
-           flush: fn _ -> :ok end}
216
+            fetch: fn
217
+              ^source_uri -> {:error, :test_error}
218
+              _ -> {:ok, mf2}
219
+            end,
220
+              flush: fn _ -> :ok end}
257 221
         ]) do
258 222
           assert {:error, :test_error} = Subject.receive!(source: source_uri, target: target_uri)
259 223
         end

+ 2
- 2
web/controllers/feed_controller.ex View File

@@ -83,7 +83,7 @@ defmodule Koype.Web.FeedController do
83 83
     end
84 84
   end
85 85
 
86
-  def activitypub(conn, _), do: Explode.not_found(conn)
87
-
88 86
   def render(conn, _), do: Explode.unprocessable_entity(conn, "Unexpected format requested.")
87
+
88
+  def activitypub(conn, _), do: Explode.not_found(conn)
89 89
 end

+ 1
- 1
web/plug/indieweb.ex View File

@@ -25,7 +25,7 @@ defmodule Koype.Web.Plug.IndieWeb do
25 25
 
26 26
   @spec call(Plug.Conn.t(), keyword()) :: Plug.Conn.t()
27 27
   def call(conn, _) do
28
-    IndieWeb.links()
28
+    Koype.links()
29 29
     |> Enum.map(&do_wrap_into_header/1)
30 30
     |> Enum.reject(&is_nil/1)
31 31
     |> Enum.reduce(conn, fn header, conn ->

+ 1
- 1
web/templates/settings/_view-webmention.html.eex View File

@@ -35,7 +35,7 @@
35 35
       {type, name} = case webmention.target do
36 36
         "/" -> {"mention", "Direct Mention"}
37 37
         path ->
38
-          {:ok, model} = IndieWeb.Webmention.resolve_target(path)
38
+          {:ok, model} = IndieWeb.Webmention.resolve_target_from_url(path)
39 39
           {model.type, model.name}
40 40
       end
41 41
     %>

+ 1
- 1
web/views/layout_view.ex View File

@@ -83,7 +83,7 @@ defmodule Koype.Web.LayoutView do
83 83
   end
84 84
 
85 85
   def tag({:link, rel}, _template, _assigns) do
86
-    links = IndieWeb.links()
86
+    links = Koype.links()
87 87
 
88 88
     case links[rel] do
89 89
       nil -> nil

Loading…
Cancel
Save