mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-08 22:17:34 +01:00
Add missing fields to packages API
This commit is contained in:
parent
12e364969b
commit
90aeb6e1a7
@ -218,19 +218,23 @@ def makeLabel(obj):
|
||||
return obj.title
|
||||
|
||||
class PackageForm(FlaskForm):
|
||||
name = StringField("Name (Technical)", [InputRequired(), Length(1, 100), Regexp("^[a-z0-9_]+$", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
||||
title = StringField("Title (Human-readable)", [InputRequired(), Length(3, 100)])
|
||||
short_desc = StringField("Short Description (Plaintext)", [InputRequired(), Length(1,200)])
|
||||
desc = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
|
||||
type = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
|
||||
license = QuerySelectField("License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
media_license = QuerySelectField("Media License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
title = StringField("Title (Human-readable)", [InputRequired(), Length(3, 100)])
|
||||
name = StringField("Name (Technical)", [InputRequired(), Length(1, 100), Regexp("^[a-z0-9_]+$", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
||||
short_desc = StringField("Short Description (Plaintext)", [InputRequired(), Length(1,200)])
|
||||
|
||||
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=makeLabel)
|
||||
content_warnings = QuerySelectMultipleField('Content Warnings', query_factory=lambda: ContentWarning.query.order_by(db.asc(ContentWarning.name)), get_pk=lambda a: a.id, get_label=makeLabel)
|
||||
license = QuerySelectField("License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
media_license = QuerySelectField("Media License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
|
||||
desc = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
|
||||
|
||||
repo = StringField("VCS Repository URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||
website = StringField("Website URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||
issueTracker = StringField("Issue Tracker URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||
forums = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
|
||||
|
||||
submit = SubmitField("Save")
|
||||
|
||||
|
||||
@ -301,15 +305,15 @@ def create_edit(author=None, name=None):
|
||||
|
||||
try:
|
||||
do_edit_package(current_user, package, wasNew, {
|
||||
"name": form.name.data,
|
||||
"title": form.title.data,
|
||||
"short_desc": form.short_desc.data,
|
||||
"desc": form.desc.data,
|
||||
"type": form.type.data,
|
||||
"license": form.license.data,
|
||||
"media_license": form.media_license.data,
|
||||
"title": form.title.data,
|
||||
"name": form.name.data,
|
||||
"short_desc": form.short_desc.data,
|
||||
"tags": form.tags.raw_data,
|
||||
"content_warnings": form.content_warnings.raw_data,
|
||||
"license": form.license.data,
|
||||
"media_license": form.media_license.data,
|
||||
"desc": form.desc.data,
|
||||
"repo": form.repo.data,
|
||||
"website": form.website.data,
|
||||
"issueTracker": form.issueTracker.data,
|
||||
|
@ -1,12 +1,29 @@
|
||||
title: API
|
||||
|
||||
## Responses and Error Handling
|
||||
|
||||
If there is an error, the response will be JSON similar to the following with a non-200 status code:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "The error message"
|
||||
}
|
||||
```
|
||||
|
||||
Successful GET requests will return the resource's information directly as a JSON response.
|
||||
|
||||
Other successful results will return a dictionary with `success` equaling true, and
|
||||
often other keys with information.
|
||||
|
||||
|
||||
## Authentication
|
||||
|
||||
Not all endpoints require authentication.
|
||||
Authentication is done using Bearer tokens:
|
||||
Not all endpoints require authentication, but it is done using Bearer tokens:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOURTOKEN" https://content.minetest.net/api/whoami/
|
||||
curl https://content.minetest.net/api/whoami/ \
|
||||
-H "Authorization: Bearer YOURTOKEN"
|
||||
```
|
||||
|
||||
Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
||||
@ -23,19 +40,37 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
||||
* See [Package Queries](#package-queries)
|
||||
* GET `/api/packages/<username>/<name>/` (Read)
|
||||
* PUT `/api/packages/<author>/<name>/` (Update)
|
||||
* JSON dictionary with any of these keys (all are optional):
|
||||
* `title`: Human-readable title.
|
||||
* `short_description`
|
||||
* `desc`
|
||||
* JSON dictionary with any of these keys (all are optional, null to delete Nullables):
|
||||
* `type`: One of `GAME`, `MOD`, `TXP`.
|
||||
* `title`: Human-readable title.
|
||||
* `name`: Technical name (needs permission if already approved).
|
||||
* `short_description`
|
||||
* `tags`: List of tag names, see [misc](#misc).
|
||||
* `content_Warnings`: List of content warning names, see [misc](#misc).
|
||||
* `license`: A license name.
|
||||
* `media_license`: A license name.
|
||||
* `media_license`: A license name.
|
||||
* `desc`: Long markdown description.
|
||||
* `repo`: Git repo URL.
|
||||
* `website`: Website URL.
|
||||
* `issue_tracker`: Issue tracker URL.
|
||||
* `forums`: forum topic ID.
|
||||
* GET `/api/packages/<username>/<name>/dependencies/`
|
||||
* If query argument `only_hard` is present, only hard deps will be returned.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# Edit packages
|
||||
curl -X PUT http://localhost:5123/api/packages/username/name/ \
|
||||
-H "Authorization: Bearer YOURTOKEN" -H "Content-Type: application/json" \
|
||||
-d '{ "title": "Foo bar", "tags": ["pvp", "survival"], "license": "wtfpl" }'
|
||||
|
||||
# Remove website URL
|
||||
curl -X PUT http://localhost:5123/api/packages/username/name/ \
|
||||
-H "Authorization: Bearer YOURTOKEN" -H "Content-Type: application/json" \
|
||||
-d '{ "website": null }'
|
||||
```
|
||||
|
||||
### Package Queries
|
||||
|
||||
Example:
|
||||
@ -93,7 +128,7 @@ Examples:
|
||||
# Create release from Git
|
||||
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
||||
-H "Authorization: Bearer YOURTOKEN" -H "Content-Type: application/json" \
|
||||
-d "{\"method\": \"git\", \"title\": \"My Release\", \"ref\": \"master\" }"
|
||||
-d '{ "method": "git", "title": "My Release", "ref": "master" }'
|
||||
|
||||
# Create release from zip upload
|
||||
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
||||
|
@ -39,14 +39,18 @@ def get_license(name):
|
||||
|
||||
name_re = re.compile("^[a-z0-9_]+$")
|
||||
|
||||
TYPES = {
|
||||
"name": str,
|
||||
any = "?"
|
||||
ALLOWED_FIELDS = {
|
||||
"type": any,
|
||||
"title": str,
|
||||
"name": str,
|
||||
"short_description": str,
|
||||
"short_desc": str,
|
||||
"desc": str,
|
||||
"tags": list,
|
||||
"content_warnings": list,
|
||||
"license": any,
|
||||
"media_license": any,
|
||||
"desc": str,
|
||||
"repo": str,
|
||||
"website": str,
|
||||
"issue_tracker": str,
|
||||
@ -63,9 +67,12 @@ def is_int(val):
|
||||
|
||||
|
||||
def validate(data: dict):
|
||||
for key, typ in TYPES.items():
|
||||
if data.get(key) is not None:
|
||||
check(isinstance(data[key], typ), key + " must be a " + typ.__name__)
|
||||
for key, value in data.items():
|
||||
if value is not None:
|
||||
typ = ALLOWED_FIELDS.get(key)
|
||||
check(typ is not None, key + " is not a known field")
|
||||
if typ != any:
|
||||
check(isinstance(value, typ), key + " must be a " + typ.__name__)
|
||||
|
||||
if "name" in data:
|
||||
name = data["name"]
|
||||
|
@ -400,6 +400,10 @@ class Package(db.Model):
|
||||
release = self.getDownloadRelease(version=version)
|
||||
return {
|
||||
"author": self.author.username,
|
||||
"maintainers": [x.username for x in self.maintainers],
|
||||
|
||||
"state": self.state.name,
|
||||
|
||||
"name": self.name,
|
||||
"title": self.title,
|
||||
"short_description": self.short_desc,
|
||||
@ -415,6 +419,9 @@ class Package(db.Model):
|
||||
"issue_tracker": self.issueTracker,
|
||||
"forums": self.forums,
|
||||
|
||||
"tags": [x.name for x in self.tags],
|
||||
"content_warnings": [x.name for x in self.content_warnings],
|
||||
|
||||
"provides": [x.name for x in self.provides],
|
||||
"thumbnail": (base_url + tnurl) if tnurl is not None else None,
|
||||
"screenshots": [base_url + ss.url for ss in self.screenshots],
|
||||
|
Loading…
Reference in New Issue
Block a user