mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-09 22:47:36 +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
|
return obj.title
|
||||||
|
|
||||||
class PackageForm(FlaskForm):
|
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)
|
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)
|
title = StringField("Title (Human-readable)", [InputRequired(), Length(3, 100)])
|
||||||
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)
|
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)
|
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)
|
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])
|
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])
|
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])
|
issueTracker = StringField("Issue Tracker URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
forums = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
|
forums = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
|
||||||
|
|
||||||
submit = SubmitField("Save")
|
submit = SubmitField("Save")
|
||||||
|
|
||||||
|
|
||||||
@ -301,15 +305,15 @@ def create_edit(author=None, name=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
do_edit_package(current_user, package, wasNew, {
|
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,
|
"type": form.type.data,
|
||||||
"license": form.license.data,
|
"title": form.title.data,
|
||||||
"media_license": form.media_license.data,
|
"name": form.name.data,
|
||||||
|
"short_desc": form.short_desc.data,
|
||||||
"tags": form.tags.raw_data,
|
"tags": form.tags.raw_data,
|
||||||
"content_warnings": form.content_warnings.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,
|
"repo": form.repo.data,
|
||||||
"website": form.website.data,
|
"website": form.website.data,
|
||||||
"issueTracker": form.issueTracker.data,
|
"issueTracker": form.issueTracker.data,
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
title: API
|
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
|
## Authentication
|
||||||
|
|
||||||
Not all endpoints require authentication.
|
Not all endpoints require authentication, but it is done using Bearer tokens:
|
||||||
Authentication is done using Bearer tokens:
|
|
||||||
|
|
||||||
```bash
|
```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/).
|
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)
|
* See [Package Queries](#package-queries)
|
||||||
* GET `/api/packages/<username>/<name>/` (Read)
|
* GET `/api/packages/<username>/<name>/` (Read)
|
||||||
* PUT `/api/packages/<author>/<name>/` (Update)
|
* PUT `/api/packages/<author>/<name>/` (Update)
|
||||||
* JSON dictionary with any of these keys (all are optional):
|
* JSON dictionary with any of these keys (all are optional, null to delete Nullables):
|
||||||
* `title`: Human-readable title.
|
|
||||||
* `short_description`
|
|
||||||
* `desc`
|
|
||||||
* `type`: One of `GAME`, `MOD`, `TXP`.
|
* `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.
|
* `license`: A license name.
|
||||||
* `media_license`: A license name.
|
* `media_license`: A license name.
|
||||||
|
* `desc`: Long markdown description.
|
||||||
* `repo`: Git repo URL.
|
* `repo`: Git repo URL.
|
||||||
* `website`: Website URL.
|
* `website`: Website URL.
|
||||||
* `issue_tracker`: Issue tracker URL.
|
* `issue_tracker`: Issue tracker URL.
|
||||||
|
* `forums`: forum topic ID.
|
||||||
* GET `/api/packages/<username>/<name>/dependencies/`
|
* GET `/api/packages/<username>/<name>/dependencies/`
|
||||||
* If query argument `only_hard` is present, only hard deps will be returned.
|
* 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
|
### Package Queries
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -93,7 +128,7 @@ Examples:
|
|||||||
# Create release from Git
|
# Create release from Git
|
||||||
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
||||||
-H "Authorization: Bearer YOURTOKEN" -H "Content-Type: application/json" \
|
-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
|
# Create release from zip upload
|
||||||
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
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_]+$")
|
name_re = re.compile("^[a-z0-9_]+$")
|
||||||
|
|
||||||
TYPES = {
|
any = "?"
|
||||||
"name": str,
|
ALLOWED_FIELDS = {
|
||||||
|
"type": any,
|
||||||
"title": str,
|
"title": str,
|
||||||
|
"name": str,
|
||||||
"short_description": str,
|
"short_description": str,
|
||||||
"short_desc": str,
|
"short_desc": str,
|
||||||
"desc": str,
|
|
||||||
"tags": list,
|
"tags": list,
|
||||||
"content_warnings": list,
|
"content_warnings": list,
|
||||||
|
"license": any,
|
||||||
|
"media_license": any,
|
||||||
|
"desc": str,
|
||||||
"repo": str,
|
"repo": str,
|
||||||
"website": str,
|
"website": str,
|
||||||
"issue_tracker": str,
|
"issue_tracker": str,
|
||||||
@ -63,9 +67,12 @@ def is_int(val):
|
|||||||
|
|
||||||
|
|
||||||
def validate(data: dict):
|
def validate(data: dict):
|
||||||
for key, typ in TYPES.items():
|
for key, value in data.items():
|
||||||
if data.get(key) is not None:
|
if value is not None:
|
||||||
check(isinstance(data[key], typ), key + " must be a " + typ.__name__)
|
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:
|
if "name" in data:
|
||||||
name = data["name"]
|
name = data["name"]
|
||||||
|
@ -400,6 +400,10 @@ class Package(db.Model):
|
|||||||
release = self.getDownloadRelease(version=version)
|
release = self.getDownloadRelease(version=version)
|
||||||
return {
|
return {
|
||||||
"author": self.author.username,
|
"author": self.author.username,
|
||||||
|
"maintainers": [x.username for x in self.maintainers],
|
||||||
|
|
||||||
|
"state": self.state.name,
|
||||||
|
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
"short_description": self.short_desc,
|
"short_description": self.short_desc,
|
||||||
@ -415,6 +419,9 @@ class Package(db.Model):
|
|||||||
"issue_tracker": self.issueTracker,
|
"issue_tracker": self.issueTracker,
|
||||||
"forums": self.forums,
|
"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],
|
"provides": [x.name for x in self.provides],
|
||||||
"thumbnail": (base_url + tnurl) if tnurl is not None else None,
|
"thumbnail": (base_url + tnurl) if tnurl is not None else None,
|
||||||
"screenshots": [base_url + ss.url for ss in self.screenshots],
|
"screenshots": [base_url + ss.url for ss in self.screenshots],
|
||||||
|
Loading…
Reference in New Issue
Block a user