187 lines
5.3 KiB
Elixir
187 lines
5.3 KiB
Elixir
|
# Pleroma: A lightweight social networking server
|
||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||
|
|
||
|
defmodule Pleroma.Web.ApiSpec.StreamingOperation do
|
||
|
alias OpenApiSpex.Operation
|
||
|
alias OpenApiSpex.Response
|
||
|
alias OpenApiSpex.Schema
|
||
|
alias Pleroma.Web.ApiSpec.Helpers
|
||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||
|
|
||
|
@spec open_api_operation(atom) :: Operation.t()
|
||
|
def open_api_operation(action) do
|
||
|
operation = String.to_existing_atom("#{action}_operation")
|
||
|
apply(__MODULE__, operation, [])
|
||
|
end
|
||
|
|
||
|
@spec streaming_operation() :: Operation.t()
|
||
|
def streaming_operation do
|
||
|
%Operation{
|
||
|
tags: ["Timelines"],
|
||
|
summary: "Establish streaming connection",
|
||
|
description: "Receive statuses in real-time via WebSocket.",
|
||
|
security: [%{"oAuth" => ["read:statuses", "read:notifications"]}],
|
||
|
parameters: [
|
||
|
Operation.parameter(:connection, :header, %Schema{type: :string}, "connection header",
|
||
|
required: true
|
||
|
),
|
||
|
Operation.parameter(:upgrade, :header, %Schema{type: :string}, "upgrade header",
|
||
|
required: true
|
||
|
),
|
||
|
Operation.parameter(
|
||
|
:"sec-websocket-key",
|
||
|
:header,
|
||
|
%Schema{type: :string},
|
||
|
"sec-websocket-key header",
|
||
|
required: true
|
||
|
),
|
||
|
Operation.parameter(
|
||
|
:"sec-websocket-version",
|
||
|
:header,
|
||
|
%Schema{type: :string},
|
||
|
"sec-websocket-version header",
|
||
|
required: true
|
||
|
)
|
||
|
],
|
||
|
responses: %{
|
||
|
101 => switching_protocols_response(),
|
||
|
200 =>
|
||
|
Operation.response(
|
||
|
"Server-sent events",
|
||
|
"application/json",
|
||
|
server_sent_events()
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp switching_protocols_response do
|
||
|
%Response{
|
||
|
description: "Switching protocols",
|
||
|
headers: %{
|
||
|
"connection" => %OpenApiSpex.Header{required: true},
|
||
|
"upgrade" => %OpenApiSpex.Header{required: true},
|
||
|
"sec-websocket-accept" => %OpenApiSpex.Header{required: true}
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp server_sent_events do
|
||
|
%Schema{
|
||
|
oneOf: [
|
||
|
update_event(),
|
||
|
status_update_event(),
|
||
|
pleroma_respond_event()
|
||
|
]
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp stream do
|
||
|
%Schema{
|
||
|
type: :array,
|
||
|
title: "Stream",
|
||
|
description: """
|
||
|
The stream identifier.
|
||
|
The first item is the name of the stream. If the stream needs a differentiator, the second item will be the corresponding identifier.
|
||
|
Currently, for the following stream types, there is a second element in the array:
|
||
|
|
||
|
- `list`: The second element is the id of the list, as a string.
|
||
|
- `hashtag`: The second element is the name of the hashtag.
|
||
|
- `public:remote:media` and `public:remote`: The second element is the domain of the corresponding instance.
|
||
|
""",
|
||
|
maxItems: 2,
|
||
|
minItems: 1,
|
||
|
items: %Schema{type: :string},
|
||
|
example: ["hashtag", "mew"]
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp get_schema(%Schema{} = schema), do: schema
|
||
|
defp get_schema(schema), do: schema.schema
|
||
|
|
||
|
defp server_sent_event_helper(name, description, type, payload, opts \\ []) do
|
||
|
payload_type = opts[:payload_type] || :json
|
||
|
has_stream = opts[:has_stream] || true
|
||
|
|
||
|
stream_properties =
|
||
|
if has_stream do
|
||
|
%{stream: stream()}
|
||
|
else
|
||
|
%{}
|
||
|
end
|
||
|
|
||
|
stream_example = if has_stream, do: %{"stream" => get_schema(stream()).example}, else: %{}
|
||
|
|
||
|
stream_required = if has_stream, do: [:stream], else: []
|
||
|
|
||
|
%Schema{
|
||
|
type: :object,
|
||
|
title: name,
|
||
|
description: description,
|
||
|
required: [:event, :payload] ++ stream_required,
|
||
|
properties:
|
||
|
%{
|
||
|
event: %Schema{
|
||
|
title: "Event type",
|
||
|
description: "Type of the event.",
|
||
|
type: :string,
|
||
|
required: true,
|
||
|
enum: [type]
|
||
|
},
|
||
|
payload:
|
||
|
if payload_type == :json do
|
||
|
%Schema{
|
||
|
title: "Event payload",
|
||
|
description: "JSON-encoded string of #{get_schema(payload).title}",
|
||
|
allOf: [payload]
|
||
|
}
|
||
|
else
|
||
|
payload
|
||
|
end
|
||
|
}
|
||
|
|> Map.merge(stream_properties),
|
||
|
example:
|
||
|
%{
|
||
|
"event" => type,
|
||
|
"payload" => get_schema(payload).example |> Jason.encode!()
|
||
|
}
|
||
|
|> Map.merge(stream_example)
|
||
|
}
|
||
|
end
|
||
|
|
||
|
defp update_event do
|
||
|
server_sent_event_helper("New status", "A newly-posted status.", "update", Status)
|
||
|
end
|
||
|
|
||
|
defp status_update_event do
|
||
|
server_sent_event_helper("Edit", "A status that was just edited", "status.update", Status)
|
||
|
end
|
||
|
|
||
|
defp pleroma_respond_event do
|
||
|
server_sent_event_helper(
|
||
|
"Server response",
|
||
|
"A response to a client-sent event.",
|
||
|
"pleroma:respond",
|
||
|
%Schema{
|
||
|
type: :object,
|
||
|
title: "Results",
|
||
|
required: [:result],
|
||
|
properties: %{
|
||
|
result: %Schema{
|
||
|
type: :string,
|
||
|
title: "Result of the request",
|
||
|
enum: ["success", "error", "ignored"]
|
||
|
},
|
||
|
error: %Schema{
|
||
|
type: :string,
|
||
|
title: "Error code",
|
||
|
description: "An error identifier. Only appears if `result` is `error`."
|
||
|
}
|
||
|
},
|
||
|
example: %{"result" => "success"}
|
||
|
}
|
||
|
)
|
||
|
end
|
||
|
end
|