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

Content Repo Module
pull/977/head
Kegsay 10 years ago
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

@ -36,11 +36,11 @@ Response format:
=================== ================= ========================================== =================== ================= ==========================================
{% 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 -%}
=================== ================= ========================================== =================== ================= ==========================================

@ -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…
Cancel
Save