Packages
indieweb
0.0.60
0.0.66
0.0.65
0.0.64
0.0.63
0.0.62
0.0.61
0.0.60
0.0.59
0.0.58
0.0.56
0.0.55
0.0.54
0.0.53
0.0.51
0.0.50
0.0.49
0.0.48
0.0.47
0.0.46
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.29
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.3
0.0.2
0.0.1
Collection of common IndieWeb utilites like authorship resolution, Webmention, post type discovery and IndieAuth.
Current section
Files
Jump to
Current section
Files
lib/indieweb/link_rel.ex
defmodule IndieWeb.LinkRel do
@moduledoc """
An interface for obtaining link relationships for a URL.
This provides an interface for extracting and working on the link relationships
available to a page.
"""
alias IndieWeb.{Http, MF2}
@doc """
Extracts the provided relationship from the page.
NOTE: This defaults to returning an empty list if it fails in any way.
"""
@spec find(binary(), binary()) :: list()
def find(url, value) do
with(
{:ok, %Http.Response{code: code} = resp} when code < 499 <- Http.get(url),
header_rels <- do_extract_rels_from_request(resp, value),
page_rels <- do_extract_rels_from_body(resp, value)
) do
header_rels
|> Enum.concat(page_rels)
|> Enum.map(&do_normalize_url(&1, url))
else
_ -> []
end
end
defp do_normalize_url(url, page_url) when is_binary(url) do
cond do
# Relative to the site itself.
String.starts_with?(url, "/") ->
%{authority: authority} = URI.parse(page_url)
authority <> url
# Relative to the current page's path.
is_nil(Map.get(URI.parse(url), :authority)) and
!String.starts_with?(url, "/") ->
page_url <> "/" <> url
# Empty path is the same URL.
url == "" ->
page_url
# It's good enough!
true ->
url
end
end
defp do_extract_rels_from_request(%Http.Response{rels: rels}, value) do
matching_rel_keys =
rels
|> Map.keys()
|> Enum.filter(fn rel_str ->
rel_str |> String.starts_with?(value)
end)
rels
|> Map.take(matching_rel_keys)
|> Map.values()
|> List.flatten()
end
defp do_extract_rels_from_body(resp, value) do
%{body: body, url: url} = resp
case MF2.parse(body, url) do
{:ok, %{"rel_urls" => rel_url_map}} ->
rel_url_map
|> Enum.filter(fn {_url, %{"rels" => rels}} -> value in rels end)
|> Enum.map(fn {key, _} -> key end)
|> Enum.uniq()
{:error, _} ->
[]
end
end
end