Skip to content

Commit 03f8dad

Browse files
committed
Move upload/download API doc from api.yaml
1 parent 8008f3c commit 03f8dad

File tree

2 files changed

+137
-204
lines changed

2 files changed

+137
-204
lines changed

api.yaml

Lines changed: 0 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -16,206 +16,6 @@ externalDocs:
1616
description: Find out more about the Oxen project
1717
url: http://oxen.io
1818
paths:
19-
/room/{roomToken}/file:
20-
post:
21-
tags: [Files]
22-
summary: "Uploads a file to a room."
23-
description: >
24-
Takes the request as binary in the body and takes other properties via submitted headers.
25-
This saves space, particularly for large uploads. The user must have upload and posting
26-
permissions for the room. The file will have a default lifetime of 1 hour, but that is
27-
extended to 15 days when the containing message referencing the uploaded file is posted.
28-
29-
30-
See also the `.../fileJson` endpoint for submitting via a json body.
31-
parameters:
32-
- $ref: "#/components/parameters/pathRoomToken"
33-
- name: X-Filename
34-
in: header
35-
description: >
36-
Suggested filename of the upload. Typically the basename of the file uploaded from the
37-
user.
38-
schema:
39-
type: string
40-
requestBody:
41-
description: The file content, in bytes.
42-
required: true
43-
content:
44-
'*/*':
45-
{}
46-
responses:
47-
200:
48-
description: successful operation
49-
content:
50-
application/json:
51-
schema:
52-
type: object
53-
properties:
54-
id:
55-
type: integer
56-
format: int64
57-
description: "The id of the file on the server."
58-
403:
59-
description: >
60-
Upload forbidden. This response code indicates that the user does not have posting
61-
and/or upload permissions in the room either because of room settings, user restriction,
62-
or because the user is banned.
63-
content:
64-
application/json:
65-
schema:
66-
type: object
67-
properties:
68-
banned:
69-
type: boolean
70-
description: >
71-
True if the upload was denied because the user is banned, omitted otherwise.
72-
noWrite:
73-
type: boolean
74-
description: >
75-
True if the upload was denied because the user does not have write access to
76-
the room (but is not banned). Omitted otherwise.
77-
noUpload:
78-
type: boolean
79-
description: >
80-
True if the upload was denied because the user does not have upload access to
81-
the room (but is not banned and has write permissions). Omitted otherwise.
82-
83-
/room/{roomToken}/fileJSON:
84-
post:
85-
tags: [Files]
86-
summary: "Uploads a file to a room using a JSON encoded body."
87-
description: >
88-
This is less efficient when a binary upload is possible because the body must be passed as
89-
base64-encoded data (which is 33% larger). The user must have upload and posting
90-
permissions for the room. [NOT YET IMPLEMENTED: The file will have a default lifetime of 1
91-
hour, but that is extended to 15 days when the containing message referencing the upload
92-
is submitted.]
93-
parameters:
94-
- $ref: "#/components/parameters/pathRoomToken"
95-
requestBody:
96-
required: true
97-
content:
98-
application/json:
99-
schema:
100-
type: object
101-
required: [filename, content]
102-
properties:
103-
filename:
104-
type: string
105-
description: "Suggested filename of the upload. Typically the basename of the file uploaded from the user."
106-
content:
107-
type: string
108-
format: byte
109-
description: The file content, in base64 encoding.
110-
responses:
111-
200:
112-
$ref: "#/paths/~1room~1%7BroomToken%7D~1file/post/responses/200"
113-
403:
114-
$ref: "#/paths/~1room~1%7BroomToken%7D~1file/post/responses/403"
115-
116-
/room/{roomToken}/file/{fileId}:
117-
get:
118-
tags: [Files]
119-
summary: "Retrieves a file from the room via JSON."
120-
description: >
121-
Retrieves a file via a fileId from the room via a JSON object response. This is noticeably
122-
less efficient (particularly for large files) than the binary version when making direct
123-
requests because the file data must be encoded using base64 encoding.
124-
parameters:
125-
- $ref: "#/components/parameters/pathRoomToken"
126-
- $ref: "#/components/parameters/pathFileId"
127-
responses:
128-
200:
129-
description: successful operation; returns the file in JSON.
130-
content:
131-
application/json:
132-
schema:
133-
type: object
134-
properties:
135-
filename:
136-
type: string
137-
description: >
138-
The suggested filename of the file. Omitted if the file was uploaded without a
139-
filename (e.g. from older clients, or clients that specify an empty filename.)
140-
size:
141-
type: integer
142-
format: int64
143-
description: >
144-
The file size, in bytes. (*Not* the length of the base64-encoded data.)
145-
uploaded:
146-
type: number
147-
format: double
148-
description: The unix timestamp when the file was uploaded.
149-
expires:
150-
type: number
151-
format: double
152-
nullable: true
153-
description: >
154-
The unix timestamp when the file is scheduled to be removed. Will be null if
155-
the attachment is permanent, such as for room images or attachments in pinned
156-
messages.
157-
403:
158-
$ref: "#/paths/~1room~1%7BroomToken%7D/get/responses/403"
159-
404:
160-
description: >
161-
The referenced file does not exist. (It may have expired, or may be invalid.)
162-
163-
/room/{roomToken}/file/{fileId}/{filename}:
164-
get:
165-
tags: [Files]
166-
summary: "Retrieves a file from the room as binary."
167-
description: >
168-
Retrieves a file via a fileId from the room, returning the file content directly as the
169-
binary response body. The filename parameter is ignored and may be empty: it is primarily
170-
included to aid in clients that want the request to include a filename, and differentiates
171-
this as a request retrieving the file itself rather than the file as a JSON response. See
172-
the version without `/filename` for a JSON-returning version.
173-
parameters:
174-
- $ref: "#/components/parameters/pathRoomToken"
175-
- $ref: "#/components/parameters/pathFileId"
176-
- name: filename
177-
in: path
178-
required: true
179-
description: >
180-
Filename if known by the requesting client, and empty otherwise. The value of this
181-
parameter is ignored by the server itself: it is included to differentiate this request
182-
from the JSON version, and so that clients may include a filename in the request URL for
183-
contexts where that is useful.
184-
schema:
185-
type: integer
186-
format: int64
187-
responses:
188-
200:
189-
description: successful operation; returns the file, in raw bytes.
190-
headers:
191-
Content-Length:
192-
description: The size of the file.
193-
schema:
194-
type: integer
195-
format: int64
196-
example: 12345
197-
Date:
198-
description: The HTTP timestamp at which the file was uploaded.
199-
schema:
200-
type: string
201-
example: "Thu, 7 Oct 2021 00:42:00 GMT"
202-
Expires:
203-
description: >
204-
The HTTP timestamp at which the file is scheduled to expire. This header is omitted
205-
if the attachment is non-expiring (e.g. for attachments in a pinned message [NOT YET
206-
IMPLEMENTED]).
207-
schema:
208-
type: string
209-
example: "Fri, 22 Oct 2021 00:42:42 GMT"
210-
content:
211-
application/octet-stream:
212-
schema:
213-
type: string
214-
format: binary
215-
403:
216-
$ref: "#/paths/~1room~1%7BroomToken%7D~1file~1%7BfileId%7D/get/responses/403"
217-
404:
218-
$ref: "#/paths/~1room~1%7BroomToken%7D~1file~1%7BfileId%7D/get/responses/404"
21919
/user/{sessionId}/permission:
22020
post:
22121
tags: [Users]

sogs/routes/rooms.py

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,70 @@ def poll_room_info(room, info_updated):
354354
@rooms.post("/room/<Room:room>/file")
355355
@auth.user_required
356356
def upload_file(room):
357-
""" upload a file to a room """
357+
"""
358+
Uploads a file to a room.
359+
360+
Takes the request as binary in the body and takes other properties (specifically the suggested
361+
filename) via submitted headers.
362+
363+
The user must have upload and posting permissions for the room. The file will have a default
364+
lifetime of 1 hour, which is extended to 15 days (by default) when a post referencing the
365+
uploaded file is posted or edited.
366+
367+
# URL Parameters
368+
369+
# Body
370+
371+
The body of the request is the raw bytes that make up the attachment body.
372+
373+
# Header parameters
374+
375+
## Content-Type
376+
377+
This should be set to application/octet-stream. If the client has a strong reason to use
378+
another content type then it may do so, but it is acceptable to always use
379+
`application/octet-stream`.
380+
381+
## Content-Disposition
382+
383+
The attachment filename should be provided via the `Content-Disposition` header of the request,
384+
encoded as URI-encoded UTF-8 as per RFC 5987. Specifically, it should be formatted as:
385+
386+
Content-Disposition: attachment; filename*=UTF-8''filename.txt
387+
388+
where `filename.txt` is a utf-8 byte sequence with any bytes not in the following list encoded
389+
using %xx URI-style encoding.
390+
391+
Non-encoded ascii characters: A-Z, a-z, 0-9, !, #, $, &, +, -, ., ^, _, `, |, ~. All other
392+
characters shall be represented as their utf-8 byte sequence.
393+
394+
For instance, a file named `my 🎂.txt` (🎂 = U+1F382, with utf-8 representation 0xF0 0x9F 0x8E
395+
0x82) should specify the filename in the header as:
396+
397+
Content-Disposition: attachment; filename*=UTF-8''my%20%f0%9f%8e%82.txt
398+
399+
Filenames are not required as they are not always available (such as when uploading a pasted
400+
image) but should be used when possible.
401+
402+
The filename, if provided, will be provided in the same format in the download header for the
403+
file.
404+
405+
# Error status codes
406+
407+
- 403 Forbidden — Returned if the current user does not have permission to post messages or
408+
upload files to the room.
409+
410+
- 404 Not Found — Returned if the room does not exist, or is configured as inaccessible (and
411+
this user doesn't have access).
412+
413+
# Return value
414+
415+
On successful upload this endpoint returns a 201 (Created) status code (*not* 200), with a JSON
416+
body containing an object with key:
417+
418+
- `id` — the numeric id of the upload. If the id is not referenced via a subsequent new post,
419+
post edit, or room image request within one hour then the attachment will be deleted.
420+
"""
358421

359422
if not room.check_upload(g.user):
360423
abort(http.FORBIDDEN)
@@ -375,11 +438,58 @@ def upload_file(room):
375438

376439

377440
@rooms.get("/room/<Room:room>/file/<int:fileId>")
378-
@rooms.get("/room/<Room:room>/file/<int:fileId>/<filename>")
379441
@auth.read_required
380-
def serve_file(room, fileId, filename=None):
442+
def serve_file(room, fileId):
381443
"""
382-
serves a file uploaded to a room
444+
Retrieves a file uploaded to the room.
445+
446+
Retrieves a file via its numeric id from the room, returning the file content directly as the
447+
binary response body. The file's suggested filename (as provided by the uploader) is provided
448+
in the Content-Disposition header, if available.
449+
450+
# URL Parameters
451+
452+
- `fileId` — The id of the attachment to download.
453+
454+
# Return value
455+
456+
On success the file content is returned as bytes in the response body. Additional information
457+
is provided via response headers:
458+
459+
## Content-Length
460+
461+
The size (in bytes) of the attachment.
462+
463+
## Content-Type
464+
465+
Always `application-octet-stream` (even if the uploader specified something else).
466+
467+
## Content-Disposition
468+
469+
This specifies the suggested filename as provided by the uploader, if present. The filename is
470+
encoded using standard RFC 5987 encoding, for example:
471+
472+
Content-Disposition: attachment; filename*=UTF-8''filename.txt
473+
474+
See [the upload endpoint](#post-roomroomfile) for filename encoding details. If the attachment
475+
was uploaded without a filename then this header will not include the filename component, i.e.:
476+
477+
Content-Disposition: attachment
478+
479+
## Date
480+
481+
The timestamp at which this file was uploaded, as a standard HTTP date.
482+
483+
## Expires
484+
485+
The timestamp at which this file is currently scheduled to expire, as a standard HTTP date.
486+
487+
# Error status codes
488+
489+
- 403 Forbidden — Returned if the current user does not have permission to read messages in the
490+
room, e.g. because they are banned or the room permissions otherwise restrict access.
491+
492+
- 404 Not Found — Returned if the attachment does not exist in this room (or has expired).
383493
"""
384494
room_file = room.get_file(fileId)
385495
if not room_file:
@@ -402,3 +512,26 @@ def serve_file(room, fileId, filename=None):
402512
return Response(
403513
response=f, status=200, content_type='application/octet-stream', headers=headers
404514
)
515+
516+
517+
@rooms.get("/room/<Room:room>/file/<int:fileId>/<filename>")
518+
@auth.read_required
519+
def serve_file_with_ignored_filename(room, fileId, filename):
520+
"""
521+
Convenience endpoint for downloading file with a filename appended to the URL.
522+
523+
This endpoint is exactly identical to the version of the endpoint without a filename: the
524+
suffixed filename in the request is simply ignored. This alias is provided only to make it
525+
slightly more convenient to construct a URL containing a known filename, such as when using
526+
command-line tools for debugging.
527+
528+
Most clients should simply use the non-suffixed endpoint instead.
529+
530+
# URL Parameters
531+
532+
- `fileId` — The id of the attachment to download.
533+
534+
- `filename` — Arbitrary filename of the attachment; this value is entirely ignored by SOGS.
535+
536+
"""
537+
return serve_file(room=room, fileId=fileId)

0 commit comments

Comments
 (0)