Skip to content

Commit

Permalink
Merge pull request #57 from nextcloud/categories
Browse files Browse the repository at this point in the history
Move categories into separate route because otherwise it duplicates a…
  • Loading branch information
LukasReschke committed Jun 26, 2016
2 parents 0581c4a + 5083dfb commit 2800786
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 65 deletions.
198 changes: 153 additions & 45 deletions docs/restapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The following API routes are present:

* :ref:`api-all-releases`

* :ref:`api-all-categories`

* :ref:`api-delete-app`

* :ref:`api-delete-release`
Expand Down Expand Up @@ -45,85 +47,91 @@ This route will return all releases to display inside Nextcloud's apps admin are
{
"id": "news",
"categories": [
{
"id": "tools",
"translations": {
"en": {
"name": "Tools"
},
"de": {
"name": "Werkzeuge"
},
"fr": {
"name": "Outil"
}
}
}
"multimedia"
],
"recommendations": 100,
"featured": false,
"userDocs": "http://127.0.0.1:8000/user",
"adminDocs": "http://127.0.0.1:8000/admin",
"developerDocs": "http://127.0.0.1:8000/dev",
"issueTracker": "http://127.0.0.1:8000/issue",
"website": "http://127.0.0.1:8000/",
"created": "2016-06-09T17:56:05.076980Z",
"lastModified": "2016-06-09T17:56:19.099038Z",
"userDocs": "https://github.com/owncloud/news/wiki#user-documentation",
"adminDocs": "https://github.com/owncloud/news#readme",
"developerDocs": "https://github.com/owncloud/news/wiki#developer-documentation",
"issueTracker": "https://github.com/owncloud/news/issues",
"website": "https://github.com/owncloud/news",
"created": "2016-06-25T16:08:56.794719Z",
"lastModified": "2016-06-25T16:49:25.326855Z",
"releases": [
{
"version": "1.9.0",
"checksum": "65e613318107bceb131af5cf8b71e773b79e1a9476506f502c8e2017b52aba15",
"version": "8.8.0",
"phpExtensions": [
{
"id": "SimpleXML",
"versionSpec": "*"
},
{
"id": "curl",
"versionSpec": "*"
},
{
"id": "iconv",
"versionSpec": "*"
},
{
"id": "libxml",
"versionSpec": ">=3.0.0 <5.0.0"
"versionSpec": ">=2.7.8"
}
],
"databases": [
{
"id": "mysql",
"versionSpec": ">=5.5.0"
},
{
"id": "pgsql",
"versionSpec": ">=9.4.0"
},
{
"id": "sqlite",
"name": "Sqlite",
"versionSpec": "*"
}
],
"shellCommands": [
"grep"
],
"phpVersionSpec": "<7.0.0",
"platformVersionSpec": ">=9.0.0",
"phpVersionSpec": ">=5.6.0",
"platformVersionSpec": ">=9.0.0 <9.2.0",
"minIntSize": 64,
"download": "https://127.0.0.1:8000/download",
"created": "2016-06-09T17:57:00.587076Z",
"lastModified": "2016-06-09T17:57:00.587238Z"
"download": "https://github.com/owncloud/news/releases/download/8.8.0/news.tar.gz",
"created": "2016-06-25T16:08:56.796646Z",
"licenses": [
"agpl"
],
"lastModified": "2016-06-25T16:49:25.319425Z",
"checksum": "909377e1a695bbaa415c10ae087ae1cc48e88066d20a5a7a8beed149e9fad3d5"
}
],
"licenses": [
{
"id": "agpl",
"name": "AGPLv3+"
}
],
"screenshots": [
{
"url": "http://feeds2.feedburner.com/blogerator"
"url": "https://example.com/news.jpg"
}
],
"translations": {
"en": {
"name": "News",
"description": "Read News"
},
"de": {
"name": "Neuigkeiten",
"description": "Nachrichten lesen"
"description": "An RSS/Atom feed reader"
}
}
},
"recommendations": 0,
"featured": false
}
]
translations
Translated fields are stored inside a translations object. They can have any size, depending on if there is a translation. If a required language is not found, you should fall back to English.

screenshots
Guaranteed to be HTTPS

download
Download archive location, guaranteed to be HTTPS

versionSpec
Required versions (minimum and maximum versions) are transformed to semantic version specs. If a field is a \*, this means that there is no version requirement. The following permutations can occur:

Expand All @@ -141,6 +149,106 @@ recommendations
featured
Simple boolean flag which will be presented to the user as "hey take a look at this app". Does not imply that it has been reviewed or we recommend it officially

categories
The string value is the category's id attribute, see :ref:`api-all-categories`

.. _api-all-categories:

Get All Categories
~~~~~~~~~~~~~~~~~~
This route will return all categories and their translations.

* **Url**: GET /api/v1/categories.json

* **Authentication**: None

* **Caching**: `ETag <https://en.wikipedia.org/wiki/HTTP_ETag>`_

* **Example CURL request**::

curl http://localhost:8000/api/v1/categories.json -H 'If-None-Match: "4-2016-06-11 10:37:24+00:00"'

* **Returns**: application/json

.. code-block:: json
[
{
"id": "games",
"translations": {
"en": {
"name": "Games",
"description": ""
},
"de": {
"name": "Spiele",
"description": ""
},
"fr": {
"name": "Jeux",
"description": ""
}
}
},
{
"id": "multimedia",
"translations": {
"en": {
"name": "Multimedia",
"description": ""
},
"de": {
"name": "Multimedia",
"description": ""
},
"fr": {
"name": "Multimedia",
"description": ""
}
}
},
{
"id": "pim",
"translations": {
"en": {
"name": "PIM",
"description": ""
},
"de": {
"name": "PIM",
"description": ""
},
"fr": {
"name": "PIM",
"description": ""
}
}
},
{
"id": "tools",
"translations": {
"en": {
"name": "Tools",
"description": ""
},
"de": {
"name": "Werkzeuge",
"description": ""
},
"fr": {
"name": "Outil",
"description": ""
}
}
}
]
translations
Translated fields are stored inside a translations object. They can have any size, depending on if there is a translation. If a required language is not found, you should fall back to English.



.. _api-delete-app:

Delete an App
Expand Down
11 changes: 1 addition & 10 deletions nextcloudappstore/core/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,16 @@ def get_version_spec(self, obj):

class DatabaseDependencySerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(source='database.id')
name = serializers.ReadOnlyField(source='database.name')
version_spec = SerializerMethodField()

class Meta:
model = DatabaseDependency
fields = ('id', 'name', 'version_spec')
fields = ('id', 'version_spec')

def get_version_spec(self, obj):
return obj.version_spec.replace(',', ' ')


class LicenseSerializer(serializers.ModelSerializer):
class Meta:
model = License
fields = ('id', 'name')


class CategorySerializer(TranslatableModelSerializer):
translations = TranslatedFieldsField(shared_model=Category)

Expand All @@ -51,7 +44,6 @@ class AppReleaseSerializer(serializers.ModelSerializer):
php_extensions = \
PhpExtensionDependencySerializer(many=True, read_only=True,
source='phpextensiondependencies')
licenses = LicenseSerializer(many=True, read_only=True)
php_version_spec = SerializerMethodField()
platform_version_spec = SerializerMethodField()

Expand All @@ -77,7 +69,6 @@ class Meta:


class AppSerializer(serializers.ModelSerializer):
categories = CategorySerializer(many=True, read_only=True)
releases = AppReleaseSerializer(many=True, read_only=True)
screenshots = ScreenshotSerializer(many=True, read_only=True)
translations = TranslatedFieldsField(shared_model=App)
Expand Down
5 changes: 4 additions & 1 deletion nextcloudappstore/core/api/v1/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.conf.urls import url
from django.views.decorators.http import etag
from nextcloudappstore.core.api.v1.views import Apps, AppReleases, app_api_etag
from nextcloudappstore.core.api.v1.views import Apps, AppReleases, \
app_api_etag, Categories, category_api_etag

urlpatterns = [
url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$',
Expand All @@ -10,4 +11,6 @@
url(r'^apps/(?P<pk>[a-z_]+)/?$', Apps.as_view(), name='app-delete'),
url(r'^apps/(?P<app>[a-z_]+)/releases/(?P<version>\d+\.\d+\.\d+)/?$',
AppReleases.as_view(), name='app-release-delete'),
url(r'^categories.json$',
etag(category_api_etag)(Categories.as_view()), name='categories'),
]
29 changes: 20 additions & 9 deletions nextcloudappstore/core/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
from nextcloudappstore.core.api.v1.release.importer import ReleaseImporter
from nextcloudappstore.core.api.v1.release.provider import AppReleaseProvider
from nextcloudappstore.core.api.v1.serializers import AppSerializer, \
AppReleaseDownloadSerializer
AppReleaseDownloadSerializer, CategorySerializer
from django.db.models import Max, Count
from nextcloudappstore.core.models import App, AppRelease
from nextcloudappstore.core.models import App, AppRelease, Category
from nextcloudappstore.core.permissions import UpdateDeletePermission
from nextcloudappstore.core.throttling import PostThrottle
from pymple import Container
from rest_framework import authentication # type: ignore
from rest_framework.generics import DestroyAPIView, \
get_object_or_404 # type: ignore
get_object_or_404, ListAPIView # type: ignore
from rest_framework.permissions import IsAuthenticated # type: ignore
from rest_framework.response import Response # type: ignore
from semantic_version import Version, Spec
Expand Down Expand Up @@ -39,20 +39,31 @@ def app_api_etag(request, version):
return '%s-%s' % (count, release_modified)


def category_api_etag(request):
category_aggr = Category.objects.aggregate(count=Count('*'),
modified=Max('last_modified'))
category_modified = category_aggr['modified']
if category_modified is None:
return None
else:
return '%s-%s' % (category_aggr['count'], category_modified)


class Categories(ListAPIView):
queryset = Category.objects.all()
serializer_class = CategorySerializer


class Apps(DestroyAPIView):
authentication_classes = (authentication.BasicAuthentication,)
permission_classes = (UpdateDeletePermission,)
serializer_class = AppSerializer
queryset = App.objects.all()

def get(self, request, *args, **kwargs):
apps = App.objects.prefetch_related('translations',
'categories__translations',
'categories', 'releases',
'screenshots',
'releases__databases',
apps = App.objects.prefetch_related('translations', 'screenshots',
'releases', 'releases__databases',
'releases__php_extensions').all()

platform_version = Version(self.kwargs['version'])

def app_filter(app):
Expand Down

0 comments on commit 2800786

Please sign in to comment.