]> git.lizzy.rs Git - cheatdb.git/commitdiff
Add API to create releases
authorrubenwardy <rw@rubenwardy.com>
Fri, 24 Jan 2020 20:21:40 +0000 (20:21 +0000)
committerrubenwardy <rw@rubenwardy.com>
Fri, 24 Jan 2020 20:21:40 +0000 (20:21 +0000)
app/blueprints/api/endpoints.py
app/blueprints/api/support.py [new file with mode: 0644]
app/flatpages/help/api.md
app/models.py

index 266bf936b3a3c1913b81a25a92910a7d699e13da..bd17bb5c0df6e6f73661457eb86cb2426157fd91 100644 (file)
@@ -19,6 +19,7 @@ from flask import *
 from flask_user import *
 from . import bp
 from .auth import is_api_authd
+from .support import error, handleCreateRelease
 from app import csrf
 from app.models import *
 from app.utils import is_package_page
@@ -71,6 +72,13 @@ def package_dependencies(package):
        return jsonify(ret)
 
 
+@bp.route("/api/packages/<author>/<name>/releases/")
+@is_package_page
+def list_releases(package):
+       releases = package.releases.filter_by(approved=True).all()
+       return jsonify([ rel.getAsDictionary() for rel in releases ])
+
+
 @bp.route("/api/topics/")
 def topics():
        qb     = QueryBuilder(request.args)
@@ -113,5 +121,25 @@ def whoami(token):
 
 @bp.route("/api/markdown/", methods=["POST"])
 @csrf.exempt
-def clean_markdown():
+def markdown():
        return render_markdown(request.data.decode("utf-8"))
+
+
+@bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"])
+@csrf.exempt
+@is_package_page
+@is_api_authd
+def create_release(token, package):
+       json = request.json
+       if json is None:
+               return error(400, "JSON post data is required")
+
+       for option in ["method", "title", "ref"]:
+               if json.get(option) is None:
+                       return error(400, option + " is required in the POST data")
+
+
+       if json["method"].lower() != "vcs":
+               return error(400, "Release-creation methods other than VCS are not supported")
+
+       return handleCreateRelease(token, package, json["title"], json["ref"])
diff --git a/app/blueprints/api/support.py b/app/blueprints/api/support.py
new file mode 100644 (file)
index 0000000..0adf3db
--- /dev/null
@@ -0,0 +1,40 @@
+from app.models import PackageRelease, db, Permission
+from app.tasks.importtasks import makeVCSRelease
+from celery import uuid
+from flask import jsonify, make_response, url_for
+import datetime
+
+
+def error(status, message):
+       return make_response(jsonify({ "success": False, "error": message }), status)
+
+
+def handleCreateRelease(token, package, title, ref):
+       if not token.canOperateOnPackage(package):
+               return error(403, "API token does not have access to the package")
+
+       if not package.checkPerm(token.owner, Permission.MAKE_RELEASE):
+               return error(403, "Permission denied. Missing MAKE_RELEASE permission")
+
+       five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=5)
+       count = package.releases.filter(PackageRelease.releaseDate > five_minutes_ago).count()
+       if count >= 2:
+               return error(429, "Too many requests, please wait before trying again")
+
+       rel = PackageRelease()
+       rel.package = package
+       rel.title   = title
+       rel.url     = ""
+       rel.task_id = uuid()
+       rel.min_rel = None
+       rel.max_rel = None
+       db.session.add(rel)
+       db.session.commit()
+
+       makeVCSRelease.apply_async((rel.id, ref), task_id=rel.task_id)
+
+       return jsonify({
+               "success": True,
+               "task": url_for("tasks.check", id=rel.task_id),
+               "release": rel.getAsDictionary()
+       })
index 95e23d2a531f1df58d5cc4a9c6609494709bd8f0..c836153c4284e5dacbbeb3011263953d1aade6bf 100644 (file)
@@ -23,6 +23,19 @@ You can use the `/api/whoami` to check authentication.
 * GET `/api/packages/` - See [Package Queries](#package-queries)
 * GET `/api/packages/<username>/<name>/`
 
+### Releases
+
+* GET `/api/packages/<username>/<name>/releases/`
+* POST `/api/packages/<username>/<name>/releases/`
+       * Requires authentication.
+       * `title`: human-readable name of the release.
+       * `method`: Must be `vcs`.
+       * `min_protocol`: minimum Minetest protocol version. See [Minetest](#minetest).
+       * `min_protocol`: maximum Minetest protocol version. See [Minetest](#minetest).
+       * If `vcs` release-creation method:
+               * `ref` - git reference.
+
+
 ### Topics
 
 * GET `/api/topics/` - Supports [Package Queries](#package-queries), and the following two options:
index 18490751459a175759f57c2e00a18ab85dc8f974..5eff2ddf41273b1e3dc8c1dc0be7e745802a8b9b 100644 (file)
@@ -522,7 +522,7 @@ class Package(db.Model):
                        "short_description": self.short_desc,
                        "desc": self.desc,
                        "type": self.type.toName(),
-                       "created_at": self.created_at,
+                       "created_at": self.created_at.isoformat(),
 
                        "license": self.license.name,
                        "media_license": self.media_license.name,
@@ -773,6 +773,18 @@ class PackageRelease(db.Model):
        # If the release is approved, then the task_id must be null and the url must be present
        CK_approval_valid = db.CheckConstraint("not approved OR (task_id IS NULL AND (url = '') IS NOT FALSE)")
 
+       def getAsDictionary(self):
+               return {
+                       "id": self.id,
+                       "title": self.title,
+                       "url": self.url if self.url != "" else None,
+                       "release_date": self.releaseDate.isoformat(),
+                       "commit": self.commit_hash,
+                       "downloads": self.downloads,
+                       "min_protocol": self.min_rel and self.min_rel.protocol,
+                       "max_protocol": self.max_rel and self.max_rel.protocol
+               }
+
        def getEditURL(self):
                return url_for("packages.edit_release",
                                author=self.package.author.username,
@@ -875,10 +887,10 @@ class APIToken(db.Model):
        package    = db.relationship("Package", foreign_keys=[package_id])
 
        def canOperateOnPackage(self, package):
-               if self.package and self.package != None:
+               if self.package and self.package != package:
                        return False
 
-               return package.owner == self.owner
+               return package.author == self.owner
 
 
 class EditRequest(db.Model):