Accept multilang polls on MastoAPI

This commit is contained in:
tusooa 2023-01-03 01:29:29 -05:00 committed by marcin mikołajczak
parent 72a2b3329e
commit b1bdbdcf05
4 changed files with 102 additions and 14 deletions

View file

@ -735,18 +735,23 @@ def poll_params do
%Schema{
nullable: true,
type: :object,
required: [:options, :expires_in],
required: [:expires_in],
properties: %{
options: %Schema{
type: :array,
items: %Schema{type: :string},
description: "Array of possible answers. Must be provided with `poll[expires_in]`."
},
options_map: %Schema{
type: :array,
items: Helpers.multilang_map_of(%Schema{type: :string}),
description: "Array of possible answers. Must be provided with `poll[expires_in]`."
},
expires_in: %Schema{
type: :integer,
nullable: true,
description:
"Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
"Duration the poll should be open, in seconds. Must be provided with `poll[options]` or `poll[options_map]`"
},
multiple: %Schema{
allOf: [BooleanLike],

View file

@ -147,22 +147,34 @@ def make_poll_data(%{"poll" => %{"expires_in" => expires_in}} = data)
|> make_poll_data()
end
def make_poll_data(%{poll: %{options: options, expires_in: expires_in}} = data)
when is_list(options) do
def make_poll_data(%{poll: %{options_map: options_map, expires_in: expires_in}} = data)
when is_list(options_map) do
limits = Config.get([:instance, :poll_limits])
is_single_language = data.poll[:is_single_language]
options = options |> Enum.uniq()
with :ok <- validate_poll_expiration(expires_in, limits),
:ok <- validate_poll_options_amount(options, limits),
:ok <- validate_poll_options_length(options, limits) do
:ok <- validate_poll_options_amount(options_map, limits),
:ok <- validate_poll_options_length(options_map, limits) do
{option_notes, emoji} =
Enum.map_reduce(options, %{}, fn option, emoji ->
note = %{
"name" => option,
"type" => "Note",
"replies" => %{"type" => "Collection", "totalItems" => 0}
}
Enum.map_reduce(options_map, %{}, fn option, emoji ->
name_attrs =
if is_single_language do
%{"name" => option["und"]}
else
%{
"name" => Pleroma.MultiLanguage.map_to_str(option, multiline: false),
"nameMap" => option
}
end
note =
%{
"type" => "Note",
"replies" => %{"type" => "Collection", "totalItems" => 0}
}
|> Map.merge(name_attrs)
{note, Map.merge(emoji, Pleroma.Emoji.Formatter.get_emoji_map(option))}
end)
@ -179,6 +191,15 @@ def make_poll_data(%{poll: %{options: options, expires_in: expires_in}} = data)
end
end
def make_poll_data(%{poll: %{options: options}} = data) when is_list(options) do
new_poll = Map.put(data.poll, :options_map, Enum.map(options, &%{"und" => &1}))
data
|> Map.put(:poll, new_poll)
|> Map.put(:is_single_language, true)
|> make_poll_data()
end
def make_poll_data(%{"poll" => poll}) when is_map(poll) do
{:error, "Invalid poll"}
end
@ -200,8 +221,11 @@ defp validate_poll_options_amount(options, %{max_options: max_options}) do
end
end
defp validate_poll_options_length(options, %{max_option_chars: max_option_chars}) do
if Enum.any?(options, &(String.length(&1) > max_option_chars)) do
defp validate_poll_options_length(options_map, %{max_option_chars: max_option_chars}) do
if Enum.any?(options_map, fn option ->
Enum.reduce(option, 0, fn {_lang, cur}, acc -> acc + String.length(cur) end)
|> Kernel.>(max_option_chars)
end) do
{:error, "Poll options cannot be longer than #{max_option_chars} characters each"}
else
:ok

View file

@ -729,4 +729,26 @@ test "adds attachments to parsed results" do
}
end
end
describe "make_poll_data/1" do
test "multilang support" do
{:ok, {poll, _}} =
Utils.make_poll_data(%{
poll: %{
options_map: [
%{"a" => "foo", "b" => "1"},
%{"a" => "bar", "c" => "2"}
],
expires_in: 600
}
})
assert %{"oneOf" => choices} = poll
assert [
%{"name" => _, "nameMap" => %{"a" => "foo", "b" => "1"}},
%{"name" => _, "nameMap" => %{"a" => "bar", "c" => "2"}}
] = choices
end
end
end

View file

@ -676,6 +676,43 @@ test "posting a poll", %{conn: conn} do
assert question.data["closed"] =~ "Z"
end
test "posting a multilang poll", %{conn: conn} do
time = NaiveDateTime.utc_now()
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "Who is the #bestgrill?",
"poll" => %{
"options_map" => [
%{"a" => "Rei", "b" => "1"},
%{"a" => "Asuka", "b" => "2"},
%{"a" => "Misato", "b" => "3"}
],
"expires_in" => 420
}
})
response = json_response_and_validate_schema(conn, 200)
assert Enum.all?(response["poll"]["options"], fn %{"title_map" => title} ->
title in [
%{"a" => "Rei", "b" => "1"},
%{"a" => "Asuka", "b" => "2"},
%{"a" => "Misato", "b" => "3"}
]
end)
assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
assert response["poll"]["expired"] == false
question = Object.get_by_id(response["poll"]["id"])
# closed contains utc timezone
assert question.data["closed"] =~ "Z"
end
test "option limit is enforced", %{conn: conn} do
limit = Config.get([:instance, :poll_limits, :max_options])