Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33101673
streaming_operation.ex
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
streaming_operation.ex
View Options
# 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.NotificationOperation
alias
Pleroma.Web.ApiSpec.Schemas.Chat
alias
Pleroma.Web.ApiSpec.Schemas.Conversation
alias
Pleroma.Web.ApiSpec.Schemas.FlakeID
alias
Pleroma.Web.ApiSpec.Schemas.Status
require
Pleroma.Constants
@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.
You can specify the access token on the query string or through the `sec-websocket-protocol` header. Using
the query string to authenticate is considered unsafe and should not be used unless you have to (e.g. to maintain
your client's compatibility with Mastodon).
You may specify a stream on the query string. If you do so and you are connecting to a stream that requires logged-in users,
you must specify the access token at the time of the connection (i.e. via query string or header).
Otherwise, you have the option to authenticate after you have established the connection through client-sent events.
The "Request body" section below describes what events clients can send through WebSocket, and the "Responses" section
describes what events server will send through WebSocket.
"""
,
security
:
[%{
"oAuth"
=>
[
"read:statuses"
,
"read:notifications"
]}],
operationId
:
"WebsocketHandler.streaming"
,
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
)
]
++
stream_params
()
++
access_token_params
(),
requestBody
:
request_body
(
"Client-sent events"
,
client_sent_events
()),
responses
:
%{
101
=>
switching_protocols_response
(),
200
=>
Operation
.
response
(
"Server-sent events"
,
"application/json"
,
server_sent_events
()
)
}
}
end
defp
stream_params
do
stream_specifier
()
|>
Enum
.
map
(
fn
{
name
,
schema
}
->
Operation
.
parameter
(
name
,
:query
,
schema
,
get_schema
(
schema
)
.
description
)
end
)
end
defp
access_token_params
do
[
Operation
.
parameter
(
:access_token
,
:query
,
token
(),
token
()
.
description
),
Operation
.
parameter
(
:"sec-websocket-protocol"
,
:header
,
token
(),
token
()
.
description
)
]
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
(),
notification_event
(),
chat_update_event
(),
follow_relationships_update_event
(),
conversation_event
(),
delete_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
=
Keyword
.
get
(
opts
,
:payload_type
,
:json
)
has_stream
=
Keyword
.
get
(
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
:
[]
payload_schema
=
if
payload_type
==
:json
do
%
Schema
{
title
:
"Event payload"
,
description
:
"JSON-encoded string of
#{
get_schema
(
payload
)
.
title
}
"
,
allOf
:
[
payload
]
}
else
payload
end
payload_example
=
if
payload_type
==
:json
do
get_schema
(
payload
)
.
example
|>
Jason
.
encode!
()
else
get_schema
(
payload
)
.
example
end
%
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
:
payload_schema
}
|>
Map
.
merge
(
stream_properties
),
example
:
%{
"event"
=>
type
,
"payload"
=>
payload_example
}
|>
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
notification_event
do
server_sent_event_helper
(
"Notification"
,
"A new notification."
,
"notification"
,
NotificationOperation
.
notification
()
)
end
defp
follow_relationships_update_event
do
server_sent_event_helper
(
"Follow relationships update"
,
"An update to follow relationships."
,
"pleroma:follow_relationships_update"
,
%
Schema
{
type
:
:object
,
title
:
"Follow relationships update"
,
required
:
[
:state
,
:follower
,
:following
],
properties
:
%{
state
:
%
Schema
{
type
:
:string
,
description
:
"Follow state of the relationship."
,
enum
:
[
"follow_pending"
,
"follow_accept"
,
"follow_reject"
,
"unfollow"
]
},
follower
:
%
Schema
{
type
:
:object
,
description
:
"Information about the follower."
,
required
:
[
:id
,
:follower_count
,
:following_count
],
properties
:
%{
id
:
FlakeID
,
follower_count
:
%
Schema
{
type
:
:integer
},
following_count
:
%
Schema
{
type
:
:integer
}
}
},
following
:
%
Schema
{
type
:
:object
,
description
:
"Information about the following person."
,
required
:
[
:id
,
:follower_count
,
:following_count
],
properties
:
%{
id
:
FlakeID
,
follower_count
:
%
Schema
{
type
:
:integer
},
following_count
:
%
Schema
{
type
:
:integer
}
}
}
},
example
:
%{
"state"
=>
"follow_pending"
,
"follower"
=>
%{
"id"
=>
"someUser1"
,
"follower_count"
=>
1
,
"following_count"
=>
1
},
"following"
=>
%{
"id"
=>
"someUser2"
,
"follower_count"
=>
1
,
"following_count"
=>
1
}
}
}
)
end
defp
chat_update_event
do
server_sent_event_helper
(
"Chat update"
,
"A new chat message."
,
"pleroma:chat_update"
,
Chat
)
end
defp
conversation_event
do
server_sent_event_helper
(
"Conversation update"
,
"An update about a conversation"
,
"conversation"
,
Conversation
)
end
defp
delete_event
do
server_sent_event_helper
(
"Delete"
,
"A status that was just deleted."
,
"delete"
,
%
Schema
{
type
:
:string
,
title
:
"Status id"
,
description
:
"Id of the deleted status"
,
allOf
:
[
FlakeID
],
example
:
"some-opaque-id"
},
payload_type
:
:string
,
has_stream
:
false
)
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
,
:type
],
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`."
},
type
:
%
Schema
{
type
:
:string
,
description
:
"Type of the request."
}
},
example
:
%{
"result"
=>
"success"
,
"type"
=>
"pleroma:authenticate"
}
},
has_stream
:
false
)
end
defp
client_sent_events
do
%
Schema
{
oneOf
:
[
subscribe_event
(),
unsubscribe_event
(),
authenticate_event
()
]
}
end
defp
request_body
(
description
,
schema
,
opts
\\
[])
do
%
OpenApiSpex.RequestBody
{
description
:
description
,
content
:
%{
"application/json"
=>
%
OpenApiSpex.MediaType
{
schema
:
schema
,
example
:
opts
[
:example
],
examples
:
opts
[
:examples
]
}
}
}
end
defp
client_sent_event_helper
(
name
,
description
,
type
,
properties
,
opts
)
do
required
=
opts
[
:required
]
||
[]
%
Schema
{
type
:
:object
,
title
:
name
,
required
:
[
:type
]
++
required
,
description
:
description
,
properties
:
%{
type
:
%
Schema
{
type
:
:string
,
enum
:
[
type
],
description
:
"Type of the event."
}
}
|>
Map
.
merge
(
properties
),
example
:
opts
[
:example
]
}
end
defp
subscribe_event
do
client_sent_event_helper
(
"Subscribe"
,
"Subscribe to a stream."
,
"subscribe"
,
stream_specifier
(),
required
:
[
:stream
],
example
:
%{
"type"
=>
"subscribe"
,
"stream"
=>
"list"
,
"list"
=>
"1"
}
)
end
defp
unsubscribe_event
do
client_sent_event_helper
(
"Unsubscribe"
,
"Unsubscribe from a stream."
,
"unsubscribe"
,
stream_specifier
(),
required
:
[
:stream
],
example
:
%{
"type"
=>
"unsubscribe"
,
"stream"
=>
"public:remote:media"
,
"instance"
=>
"example.org"
}
)
end
defp
authenticate_event
do
client_sent_event_helper
(
"Authenticate"
,
"Authenticate via an access token."
,
"pleroma:authenticate"
,
%{
token
:
token
()
},
required
:
[
:token
]
)
end
defp
token
do
%
Schema
{
type
:
:string
,
description
:
"An OAuth access token with corresponding permissions."
,
example
:
"some token"
}
end
defp
stream_specifier
do
%{
stream
:
%
Schema
{
type
:
:string
,
description
:
"The name of the stream."
,
enum
:
Pleroma.Constants
.
public_streams
()
++
[
"public:remote"
,
"public:remote:media"
,
"user"
,
"user:pleroma_chat"
,
"user:notification"
,
"direct"
,
"list"
,
"hashtag"
]
},
list
:
%
Schema
{
type
:
:string
,
title
:
"List id"
,
description
:
"The id of the list. Required when `stream` is `list`."
,
example
:
"some-id"
},
tag
:
%
Schema
{
type
:
:string
,
title
:
"Hashtag name"
,
description
:
"The name of the hashtag. Required when `stream` is `hashtag`."
,
example
:
"mew"
},
instance
:
%
Schema
{
type
:
:string
,
title
:
"Domain name"
,
description
:
"Domain name of the instance. Required when `stream` is `public:remote` or `public:remote:media`."
,
example
:
"example.org"
}
}
end
end
File Metadata
Details
Attached
Mime Type
text/x-ruby
Expires
Tue, Jan 20, 12:44 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
973580
Default Alt Text
streaming_operation.ex (12 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment