Merge pull request #83 from matrix-org/module-content-repo

Content Repo Module
pull/977/head
Kegsay 9 years ago
commit 68df99409a

@ -34,7 +34,7 @@ def check_parameter(filepath, request, parameter):
example = None
try:
example_json = schema.get('example')
if example_json:
if example_json and not schema.get("format") == "byte":
example = json.loads(example_json)
except Exception as e:
raise ValueError("Error parsing JSON example request for %r" % (

@ -15,16 +15,22 @@ paths:
summary: Upload some content to the content repository.
produces: ["application/json"]
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
name: content
name: "<content>"
description: The content to be uploaded.
required: true
schema:
type: string
example: "<bytes>"
format: byte
responses:
200:
description: Information about the uploaded content.
description: The MXC URI for the uploaded content.
schema:
type: object
required: ["content_uri"]
@ -32,6 +38,11 @@ paths:
content_uri:
type: string
description: "The MXC URI to the uploaded content."
examples:
"application/json": |-
{
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
}
"/download/{serverName}/{mediaId}":
get:
summary: "Download content from the content repository."
@ -40,18 +51,27 @@ paths:
- in: path
type: string
name: serverName
x-example: matrix.org
required: true
description: |
The server name from the ``mxc://`` URI (the authoritory component)
- in: path
type: string
name: mediaId
x-example: ascERGshawAWawugaAcauga
required: true
description: |
The media ID from the ``mxc://`` URI (the path component)
responses:
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:
type: file
"/thumbnail/{serverName}/{mediaId}":
@ -63,30 +83,44 @@ paths:
type: string
name: serverName
required: true
x-example: matrix.org
description: |
The server name from the ``mxc://`` URI (the authoritory component)
- in: path
type: string
name: mediaId
x-example: ascERGshawAWawugaAcauga
required: true
description: |
The media ID from the ``mxc://`` URI (the path component)
- in: query
type: integer
x-example: 64
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
type: integer
x-example: 64
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
type: string
enum: ["crop", "scale"]
name: method
x-example: "scale"
description: The desired resizing method.
responses:
200:
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:
type: file

@ -10,6 +10,6 @@
"license": "ISC",
"dependencies": {
"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) {
return;
}
console.log(metadata);
console.error(err);
process.exit(1);
};
@ -46,11 +45,12 @@ if (isDir) {
files.forEach(function(f) {
var suffix = ".yaml";
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) {
console.log("%s is valid.", f);
}
else {
console.error("%s is not valid.", f);
errFn(err, api, metadata);
}
});
@ -59,12 +59,12 @@ if (isDir) {
});
}
else{
parser.parse(opts.schema, function(err, api, metadata) {
parser.validate(opts.schema, function(err, api) {
if (!err) {
console.log("%s is valid", opts.schema);
}
else {
errFn(err, api, metadata);
errFn(err, api);
}
});
};

@ -3,44 +3,31 @@ Content repository
.. _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
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::
mxc://<server-name>/<media-id>
=> POST /_matrix/media/v1/upload HTTP/1.1
Content-Type: <media-type>
<server-name> : The name of the homeserver where this content originated, e.g. matrix.org
<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
Content-Type: application/json
Client behaviour
----------------
{ "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
<= 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>
{{content_repo_http_api}}
Thumbnails
~~~~~~~~~~
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
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
within a given rectangle.
Server behaviour
----------------
Homeservers may generate thumbnails for content uploaded to remote
homeservers themselves or may rely on the remote homeserver to thumbnail
the content. Homeservers may return thumbnails of a different size to that
@ -58,13 +48,19 @@ Homeservers must never upscale images.
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
that are too large and should not serve them to clients.
- Clients may try to upload very large images. Homeservers should not attempt to
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.
- 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 -%}
{{"``"+table.title+"``" if table.title else "" }}
================== ================= ===========================================
=================== ================= ==========================================
Param Type Description
================== ================= ===========================================
=================== ================= ==========================================
{% 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 -#}
{# 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 %}
{% endif -%}

@ -168,6 +168,13 @@ class MatrixUnits(Units):
# assign value expected for this param
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
schemaFmt = Units.prop(param, "schema/format") # bytes e.g. uploads
if not val_type and refType:
@ -270,17 +277,27 @@ class MatrixUnits(Units):
if good_response:
self.log("Found a 200 response for this API")
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"]:
# response is a raw string or something like that
endpoint["res_tables"].append({
good_table = {
"title": None,
"rows": [{
"key": good_response["schema"].get("name", ""),
"key": "<" + res_type + ">" if not res_name else res_name,
"type": res_type,
"desc": res.get("description", ""),
"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"):
# response is an object:
schema = good_response["schema"]
@ -352,7 +369,7 @@ class MatrixUnits(Units):
self.log("Reading swagger API: %s" % filename)
with open(os.path.join(path, filename), "r") as f:
# strip .yaml
group_name = filename[:-5]
group_name = filename[:-5].replace("-", "_")
if is_v2:
group_name = "v2_" + group_name
api = yaml.load(f.read())

Loading…
Cancel
Save