Assets
Some content does not fit in a JSON request. Test data arrives as archives, statements contain pictures, and a course may link to a PDF of tens of megabytes.
Files like these are not sent to the API that needs them. They go to the Asset API first, which stores the file and returns a URL. From then on the URL stands in for the file: it is passed to the problem, the statement, or the course material. There is one upload endpoint to learn, one kind of value to pass around, and no other API has to accept a 200 MB archive.
Asset URLs
An upload returns a link of this form:
https://eolympusercontent.com/files/2f9c1a7b4e.zip
Images and videos are served under /images/ and /videos/; everything else under /files/. An asset belongs to the space it was uploaded to.
The link is the only value other APIs require. Attaching it to a problem, a statement or a course material passes a string rather than re-uploading a file, so one asset can be referenced in any number of places.
The same link works inside rich content:
\includegraphics[width=12cm]{https://eolympusercontent.com/images/2f9c1a7b4e}The statement editor inserts these links when an image is pasted. Content written through the API must carry the links directly.
Upload methods and limits
The methods below are addressed against the space, so the base URL is https://api.eolymp.com/spaces/<space-id>, and uploads require the asset:asset:write scope.
UploadAsset sends the whole file in one request and returns the URL. There is nothing to orchestrate, and it is the right choice up to 5 MB.
Larger files are uploaded in parts, which takes three calls between them:
- StartMultipartUpload opens the upload and returns an
upload_id. - UploadPart sends one chunk and returns a token for it.
- CompleteMultipartUpload assembles the parts and returns the URL.
Every part but the last must be exactly 5 MB, and an upload may use up to 1000 of them, so the ceiling sits a little under 5 GB. Uploads by space users are also capped at 1 GiB a day.
The API Reference documents the rest of the Asset API.
Single request upload
The Python SDK mirrors the API, so a small upload is a single call:
pip install eolympimport os
import eolymp.asset
import eolymp.core
space_url = "https://api.eolymp.com/spaces/your-space-id"
transport = eolymp.core.HttpClient(token=os.environ["EOLYMP_TOKEN"])
assets = eolymp.asset.AssetServiceClient(transport, url=space_url)
with open("sample-layout.png", "rb") as f:
out = assets.UploadAsset(request=eolymp.asset.UploadAssetInput(
name="sample-layout.png",
data=f.read(),
))
print(out.asset_url) # https://eolympusercontent.com/images/...Set the name: Eolymp reads the extension to determine the content type, and uses the name again when a browser downloads the file. If the extension is misleading, set type to the intended MIME type.
Multipart upload
Past 5 MB an upload goes through the multipart sequence: start it, send the parts, complete it. Each part comes back with a token, and the final call needs all of them together with their part numbers.
import itertools
import os
import eolymp.asset
import eolymp.core
space_url = "https://api.eolymp.com/spaces/your-space-id"
PART_SIZE = 5 * 1024 * 1024 # every part but the last must be exactly this
transport = eolymp.core.HttpClient(token=os.environ["EOLYMP_TOKEN"])
assets = eolymp.asset.AssetServiceClient(transport, url=space_url)
start = assets.StartMultipartUpload(request=eolymp.asset.StartMultipartUploadInput(
name="tests.zip",
type="application/zip",
))
parts = []
with open("tests.zip", "rb") as f:
for number in itertools.count(1):
chunk = f.read(PART_SIZE)
if not chunk:
break
part = assets.UploadPart(request=eolymp.asset.UploadPartInput(
upload_id=start.upload_id,
part_number=number,
data=chunk,
))
parts.append(eolymp.asset.CompleteMultipartUploadInput.Part(
number=number,
token=part.token,
))
out = assets.CompleteMultipartUpload(request=eolymp.asset.CompleteMultipartUploadInput(
upload_id=start.upload_id,
parts=parts,
))
print(out.asset_url)Part numbers start at 1. Nothing is visible at a URL until the upload is completed, so a failed part can be sent again before finishing. Only the account that started an upload can complete it.
Asset lookup
When files are synced from another system, keep a keys list on the upload holding the identifiers used in that system:
out = assets.UploadAsset(request=eolymp.asset.UploadAssetInput(
name="tests.zip",
keys=["polygon:problem-1234:tests"],
data=data,
))The URL can then be requested by key instead of being stored separately:
found = assets.LookupAsset(request=eolymp.asset.LookupAssetInput(
key="polygon:problem-1234:tests",
))
print(found.asset_url)This keeps a re-import from uploading the same archive twice. Lookups need the asset:asset:read scope.
Updated 8 days ago
