mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-03-09 21:14:09 +01:00
Merge pull request #83 from matrix-org/module-content-repo
Content Repo Module
This commit is contained in:
commit
68df99409a
|
|
@ -34,7 +34,7 @@ def check_parameter(filepath, request, parameter):
|
||||||
example = None
|
example = None
|
||||||
try:
|
try:
|
||||||
example_json = schema.get('example')
|
example_json = schema.get('example')
|
||||||
if example_json:
|
if example_json and not schema.get("format") == "byte":
|
||||||
example = json.loads(example_json)
|
example = json.loads(example_json)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError("Error parsing JSON example request for %r" % (
|
raise ValueError("Error parsing JSON example request for %r" % (
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,22 @@ paths:
|
||||||
summary: Upload some content to the content repository.
|
summary: Upload some content to the content repository.
|
||||||
produces: ["application/json"]
|
produces: ["application/json"]
|
||||||
parameters:
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: Content-Type
|
||||||
|
type: string
|
||||||
|
description: The content type of the file being uploaded
|
||||||
|
x-example: "Content-Type: audio/mpeg"
|
||||||
- in: body
|
- in: body
|
||||||
name: content
|
name: "<content>"
|
||||||
description: The content to be uploaded.
|
description: The content to be uploaded.
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
example: "<bytes>"
|
||||||
format: byte
|
format: byte
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Information about the uploaded content.
|
description: The MXC URI for the uploaded content.
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required: ["content_uri"]
|
required: ["content_uri"]
|
||||||
|
|
@ -32,6 +38,11 @@ paths:
|
||||||
content_uri:
|
content_uri:
|
||||||
type: string
|
type: string
|
||||||
description: "The MXC URI to the uploaded content."
|
description: "The MXC URI to the uploaded content."
|
||||||
|
examples:
|
||||||
|
"application/json": |-
|
||||||
|
{
|
||||||
|
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
|
||||||
|
}
|
||||||
"/download/{serverName}/{mediaId}":
|
"/download/{serverName}/{mediaId}":
|
||||||
get:
|
get:
|
||||||
summary: "Download content from the content repository."
|
summary: "Download content from the content repository."
|
||||||
|
|
@ -40,18 +51,27 @@ paths:
|
||||||
- in: path
|
- in: path
|
||||||
type: string
|
type: string
|
||||||
name: serverName
|
name: serverName
|
||||||
|
x-example: matrix.org
|
||||||
required: true
|
required: true
|
||||||
description: |
|
description: |
|
||||||
The server name from the ``mxc://`` URI (the authoritory component)
|
The server name from the ``mxc://`` URI (the authoritory component)
|
||||||
- in: path
|
- in: path
|
||||||
type: string
|
type: string
|
||||||
name: mediaId
|
name: mediaId
|
||||||
|
x-example: ascERGshawAWawugaAcauga
|
||||||
required: true
|
required: true
|
||||||
description: |
|
description: |
|
||||||
The media ID from the ``mxc://`` URI (the path component)
|
The media ID from the ``mxc://`` URI (the path component)
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "The content downloaded."
|
description: "The content that was previously uploaded."
|
||||||
|
headers:
|
||||||
|
Content-Type:
|
||||||
|
description: "The content type of the file that was previously uploaded."
|
||||||
|
type: "string"
|
||||||
|
Content-Disposition:
|
||||||
|
description: "The name of the file that was previously uploaded, if set."
|
||||||
|
type: "string"
|
||||||
schema:
|
schema:
|
||||||
type: file
|
type: file
|
||||||
"/thumbnail/{serverName}/{mediaId}":
|
"/thumbnail/{serverName}/{mediaId}":
|
||||||
|
|
@ -63,30 +83,44 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
name: serverName
|
name: serverName
|
||||||
required: true
|
required: true
|
||||||
|
x-example: matrix.org
|
||||||
description: |
|
description: |
|
||||||
The server name from the ``mxc://`` URI (the authoritory component)
|
The server name from the ``mxc://`` URI (the authoritory component)
|
||||||
- in: path
|
- in: path
|
||||||
type: string
|
type: string
|
||||||
name: mediaId
|
name: mediaId
|
||||||
|
x-example: ascERGshawAWawugaAcauga
|
||||||
required: true
|
required: true
|
||||||
description: |
|
description: |
|
||||||
The media ID from the ``mxc://`` URI (the path component)
|
The media ID from the ``mxc://`` URI (the path component)
|
||||||
- in: query
|
- in: query
|
||||||
type: integer
|
type: integer
|
||||||
|
x-example: 64
|
||||||
name: width
|
name: width
|
||||||
description: The desired width of the thumbnail.
|
description: |-
|
||||||
|
The *desired* width of the thumbnail. The actual thumbnail may not
|
||||||
|
match the size specified.
|
||||||
- in: query
|
- in: query
|
||||||
type: integer
|
type: integer
|
||||||
|
x-example: 64
|
||||||
name: height
|
name: height
|
||||||
description: The desired height of the thumbnail.
|
description: |-
|
||||||
|
The *desired* height of the thumbnail. The actual thumbnail may not
|
||||||
|
match the size specified.
|
||||||
- in: query
|
- in: query
|
||||||
type: string
|
type: string
|
||||||
enum: ["crop", "scale"]
|
enum: ["crop", "scale"]
|
||||||
name: method
|
name: method
|
||||||
|
x-example: "scale"
|
||||||
description: The desired resizing method.
|
description: The desired resizing method.
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "A thumbnail of the requested content."
|
description: "A thumbnail of the requested content."
|
||||||
|
headers:
|
||||||
|
Content-Type:
|
||||||
|
description: "The content type of the thumbnail."
|
||||||
|
type: "string"
|
||||||
|
enum: ["image/jpeg", "image/png"]
|
||||||
schema:
|
schema:
|
||||||
type: file
|
type: file
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nopt": "^3.0.2",
|
"nopt": "^3.0.2",
|
||||||
"swagger-parser": "^2.4.1"
|
"swagger-parser": "^3.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,10 @@ if (!opts.schema) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var errFn = function(err, api, metadata) {
|
var errFn = function(err, api) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(metadata);
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
};
|
};
|
||||||
|
|
@ -46,11 +45,12 @@ if (isDir) {
|
||||||
files.forEach(function(f) {
|
files.forEach(function(f) {
|
||||||
var suffix = ".yaml";
|
var suffix = ".yaml";
|
||||||
if (f.indexOf(suffix, f.length - suffix.length) > 0) {
|
if (f.indexOf(suffix, f.length - suffix.length) > 0) {
|
||||||
parser.parse(path.join(opts.schema, f), function(err, api, metadata) {
|
parser.validate(path.join(opts.schema, f), function(err, api, metadata) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
console.log("%s is valid.", f);
|
console.log("%s is valid.", f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
console.error("%s is not valid.", f);
|
||||||
errFn(err, api, metadata);
|
errFn(err, api, metadata);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -59,12 +59,12 @@ if (isDir) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
parser.parse(opts.schema, function(err, api, metadata) {
|
parser.validate(opts.schema, function(err, api) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
console.log("%s is valid", opts.schema);
|
console.log("%s is valid", opts.schema);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
errFn(err, api, metadata);
|
errFn(err, api);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,44 +3,31 @@ Content repository
|
||||||
|
|
||||||
.. _module:content:
|
.. _module:content:
|
||||||
|
|
||||||
HTTP API
|
This module allows users to upload content to their homeserver which is
|
||||||
--------
|
retrievable from other homeservers. Its' purpose is to allow users to share
|
||||||
|
attachments in a room. Content locations are represented as Matrix Content (MXC)
|
||||||
|
URIs. They look like::
|
||||||
|
|
||||||
Uploads are POSTed to a resource which returns a token which is used to GET
|
mxc://<server-name>/<media-id>
|
||||||
the download. Uploads are POSTed to the sender's local homeserver, but are
|
|
||||||
downloaded from the recipient's local homeserver, which must thus first transfer
|
|
||||||
the content from the origin homeserver using the same API (unless the origin
|
|
||||||
and destination homeservers are the same). The upload/download API is::
|
|
||||||
|
|
||||||
=> POST /_matrix/media/v1/upload HTTP/1.1
|
<server-name> : The name of the homeserver where this content originated, e.g. matrix.org
|
||||||
Content-Type: <media-type>
|
<media-id> : An opaque ID which identifies the content.
|
||||||
|
|
||||||
<media>
|
Uploads are POSTed to a resource on the user's local homeserver which returns a
|
||||||
|
token which is used to GET the download. Content is downloaded from the
|
||||||
|
recipient's local homeserver, which must first transfer the content from the
|
||||||
|
origin homeserver using the same API (unless the origin and destination
|
||||||
|
homeservers are the same).
|
||||||
|
|
||||||
<= HTTP/1.1 200 OK
|
Client behaviour
|
||||||
Content-Type: application/json
|
----------------
|
||||||
|
|
||||||
{ "content-uri": "mxc://<server-name>/<media-id>" }
|
Clients can upload and download content using the following HTTP APIs.
|
||||||
|
|
||||||
=> GET /_matrix/media/v1/download/<server-name>/<media-id> HTTP/1.1
|
{{content_repo_http_api}}
|
||||||
|
|
||||||
<= HTTP/1.1 200 OK
|
|
||||||
Content-Type: <media-type>
|
|
||||||
Content-Disposition: attachment;filename=<upload-filename>
|
|
||||||
|
|
||||||
<media>
|
|
||||||
|
|
||||||
Clients can get thumbnails by supplying a desired width and height and
|
|
||||||
thumbnailing method::
|
|
||||||
|
|
||||||
=> GET /_matrix/media/v1/thumbnail/<server_name>
|
|
||||||
/<media-id>?width=<w>&height=<h>&method=<m> HTTP/1.1
|
|
||||||
|
|
||||||
<= HTTP/1.1 200 OK
|
|
||||||
Content-Type: image/jpeg or image/png
|
|
||||||
|
|
||||||
<thumbnail>
|
|
||||||
|
|
||||||
|
Thumbnails
|
||||||
|
~~~~~~~~~~
|
||||||
The thumbnail methods are "crop" and "scale". "scale" tries to return an
|
The thumbnail methods are "crop" and "scale". "scale" tries to return an
|
||||||
image where either the width or the height is smaller than the requested
|
image where either the width or the height is smaller than the requested
|
||||||
size. The client should then scale and letterbox the image if it needs to
|
size. The client should then scale and letterbox the image if it needs to
|
||||||
|
|
@ -49,6 +36,9 @@ width and height are close to the requested size and the aspect matches
|
||||||
the requested size. The client should scale the image if it needs to fit
|
the requested size. The client should scale the image if it needs to fit
|
||||||
within a given rectangle.
|
within a given rectangle.
|
||||||
|
|
||||||
|
Server behaviour
|
||||||
|
----------------
|
||||||
|
|
||||||
Homeservers may generate thumbnails for content uploaded to remote
|
Homeservers may generate thumbnails for content uploaded to remote
|
||||||
homeservers themselves or may rely on the remote homeserver to thumbnail
|
homeservers themselves or may rely on the remote homeserver to thumbnail
|
||||||
the content. Homeservers may return thumbnails of a different size to that
|
the content. Homeservers may return thumbnails of a different size to that
|
||||||
|
|
@ -58,13 +48,19 @@ Homeservers must never upscale images.
|
||||||
Security considerations
|
Security considerations
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
The HTTP GET endpoint does not require any authentication. Knowing the URL of
|
||||||
|
the content is sufficient to retrieve the content, even if the entity isn't in
|
||||||
|
the room.
|
||||||
|
|
||||||
|
Homeservers have additional concerns:
|
||||||
|
|
||||||
- Clients may try to upload very large files. Homeservers should not store files
|
- Clients may try to upload very large files. Homeservers should not store files
|
||||||
that are too large and should not serve them to clients.
|
that are too large and should not serve them to clients.
|
||||||
|
|
||||||
- Clients may try to upload very large images. Homeservers should not attempt to
|
- Clients may try to upload very large images. Homeservers should not attempt to
|
||||||
generate thumbnails for images that are too large.
|
generate thumbnails for images that are too large.
|
||||||
|
|
||||||
- Remote homeservers may host very large files or images. Homeserver should not
|
- Remote homeservers may host very large files or images. Homeservers should not
|
||||||
proxy or thumbnail large files or images from remote homeservers.
|
proxy or thumbnail large files or images from remote homeservers.
|
||||||
|
|
||||||
- Clients may try to upload a large number of files. Homeservers should limit the
|
- Clients may try to upload a large number of files. Homeservers should limit the
|
||||||
|
|
|
||||||
|
|
@ -31,18 +31,18 @@ Response format:
|
||||||
{% for table in endpoint.res_tables -%}
|
{% for table in endpoint.res_tables -%}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
{{"``"+table.title+"``" if table.title else "" }}
|
||||||
|
|
||||||
================== ================= ===========================================
|
=================== ================= ==========================================
|
||||||
Param Type Description
|
Param Type Description
|
||||||
================== ================= ===========================================
|
=================== ================= ==========================================
|
||||||
{% for row in table.rows -%}
|
{% for row in table.rows -%}
|
||||||
{# -#}
|
{# -#}
|
||||||
{# Row type needs to prepend spaces to line up with the type column (19 ch) -#}
|
{# Row type needs to prepend spaces to line up with the type column (20 ch) -#}
|
||||||
{# Desc needs to prepend the required text (maybe) and prepend spaces too -#}
|
{# Desc needs to prepend the required text (maybe) and prepend spaces too -#}
|
||||||
{# It also needs to then wrap inside the desc col (43 ch width) -#}
|
{# It also needs to then wrap inside the desc col (42 ch width) -#}
|
||||||
{# -#}
|
{# -#}
|
||||||
{{row.key}}{{row.type|indent(19-row.key|length)}}{{row.desc|wrap(43,row.req_str | indent(18 - (row.type|length))) |indent_block(37)}}
|
{{row.key}}{{row.type|indent(20-row.key|length)}}{{row.desc|wrap(42,row.req_str | indent(18 - (row.type|length))) |indent_block(38)}}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
================== ================= ===========================================
|
=================== ================= ==========================================
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,13 @@ class MatrixUnits(Units):
|
||||||
|
|
||||||
# assign value expected for this param
|
# assign value expected for this param
|
||||||
val_type = param.get("type") # integer/string
|
val_type = param.get("type") # integer/string
|
||||||
|
|
||||||
|
if param.get("enum"):
|
||||||
|
val_type = "enum"
|
||||||
|
desc += (
|
||||||
|
" One of: %s" % json.dumps(param.get("enum"))
|
||||||
|
)
|
||||||
|
|
||||||
refType = Units.prop(param, "schema/$ref/") # Error,Event
|
refType = Units.prop(param, "schema/$ref/") # Error,Event
|
||||||
schemaFmt = Units.prop(param, "schema/format") # bytes e.g. uploads
|
schemaFmt = Units.prop(param, "schema/format") # bytes e.g. uploads
|
||||||
if not val_type and refType:
|
if not val_type and refType:
|
||||||
|
|
@ -270,17 +277,27 @@ class MatrixUnits(Units):
|
||||||
if good_response:
|
if good_response:
|
||||||
self.log("Found a 200 response for this API")
|
self.log("Found a 200 response for this API")
|
||||||
res_type = Units.prop(good_response, "schema/type")
|
res_type = Units.prop(good_response, "schema/type")
|
||||||
|
res_name = Units.prop(good_response, "schema/name")
|
||||||
if res_type and res_type not in ["object", "array"]:
|
if res_type and res_type not in ["object", "array"]:
|
||||||
# response is a raw string or something like that
|
# response is a raw string or something like that
|
||||||
endpoint["res_tables"].append({
|
good_table = {
|
||||||
"title": None,
|
"title": None,
|
||||||
"rows": [{
|
"rows": [{
|
||||||
"key": good_response["schema"].get("name", ""),
|
"key": "<" + res_type + ">" if not res_name else res_name,
|
||||||
"type": res_type,
|
"type": res_type,
|
||||||
"desc": res.get("description", ""),
|
"desc": res.get("description", ""),
|
||||||
"req_str": ""
|
"req_str": ""
|
||||||
}]
|
}]
|
||||||
})
|
}
|
||||||
|
if good_response.get("headers"):
|
||||||
|
for (header_name, header) in good_response.get("headers").iteritems():
|
||||||
|
good_table["rows"].append({
|
||||||
|
"key": header_name,
|
||||||
|
"type": "Header<" + header["type"] + ">",
|
||||||
|
"desc": header["description"],
|
||||||
|
"req_str": ""
|
||||||
|
})
|
||||||
|
endpoint["res_tables"].append(good_table)
|
||||||
elif res_type and Units.prop(good_response, "schema/properties"):
|
elif res_type and Units.prop(good_response, "schema/properties"):
|
||||||
# response is an object:
|
# response is an object:
|
||||||
schema = good_response["schema"]
|
schema = good_response["schema"]
|
||||||
|
|
@ -352,7 +369,7 @@ class MatrixUnits(Units):
|
||||||
self.log("Reading swagger API: %s" % filename)
|
self.log("Reading swagger API: %s" % filename)
|
||||||
with open(os.path.join(path, filename), "r") as f:
|
with open(os.path.join(path, filename), "r") as f:
|
||||||
# strip .yaml
|
# strip .yaml
|
||||||
group_name = filename[:-5]
|
group_name = filename[:-5].replace("-", "_")
|
||||||
if is_v2:
|
if is_v2:
|
||||||
group_name = "v2_" + group_name
|
group_name = "v2_" + group_name
|
||||||
api = yaml.load(f.read())
|
api = yaml.load(f.read())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue