Skip to content

Commit

Permalink
Merge pull request #37 from nextcloud/release_ordering
Browse files Browse the repository at this point in the history
order app releases by version descending per default
  • Loading branch information
BernhardPosselt committed Jun 21, 2016
2 parents 41ee0ef + 53ddc05 commit 9d37161
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 304 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ manage=$(python) $(CURDIR)/manage.py

lint:
$(pycodestyle) $(CURDIR)/nextcloudappstore --exclude=migrations
$(mypy) --disallow-untyped-defs $(CURDIR)/nextcloudappstore/core/api/v1/release
$(mypy) --silent-imports --disallow-untyped-defs $(CURDIR)/nextcloudappstore/core/api/v1/release

test: lint
$(manage) test
Expand Down
21 changes: 11 additions & 10 deletions docs/restapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,21 @@ This route will return all releases to display inside Nextcloud's apps admin are
"phpExtensions": [
{
"id": "libxml",
"minVersion": "3",
"maxVersion": "4"
"versionSpec": ">=3.0.0 <5.0.0"
}
],
"databases": [
{
"id": "sqlite",
"name": "Sqlite",
"minVersion": "1",
"maxVersion": "2"
"versionSpec": "*"
}
],
"shellCommands": [
"grep"
],
"phpMinVersion": "5.6",
"phpMaxVersion": "",
"platformMinVersion": "9.0",
"platformMaxVersion": "",
"phpVersionSpec": "<7.0.0",
"platformVersionSpec": ">=9.0.0",
"minIntSize": 64,
"download": "https://127.0.0.1:8000/download",
"created": "2016-06-09T17:57:00.587076Z",
Expand Down Expand Up @@ -128,8 +124,13 @@ This route will return all releases to display inside Nextcloud's apps admin are
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.

min-version, max-version
Required versions (minimum and maximum versions) are stored in String fields. If a field is empty, this means that there is no version requirement. Minimum and maximum versions are inclusive, e.g. if your app works only on PHP 5.5 and 5.6, the minimum version will be 5.5.0 while the maximum version will be 5.6.*
versionSpec
Required versions (minimum and maximum versions) are transformed to semantic version specs. If a field is empty, this means that there is no version requirement. The following permutions can occur:

* **All versions**: *
* **Maximum version only**: <8.1.2
* **Minimum version only**: >=9.3.2
* **Maximum and minimum version**: >=9.3.2 <8.1.2

checksum
The checksum is generated by running sha256sum over the downloaded archive.
Expand Down
43 changes: 29 additions & 14 deletions nextcloudappstore/core/api/v1/release/importer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Dict, Any, Set # type: ignore
from nextcloudappstore.core.versioning import to_spec
from semantic_version import Version # type: ignore

from nextcloudappstore.core.models import App, Screenshot, Category, \
AppRelease, ShellCommand, License, Database, DatabaseDependency, \
PhpExtensionDependency, PhpExtension
Expand Down Expand Up @@ -42,27 +42,25 @@ def __init__(self) -> None:
class PhpExtensionImporter(ScalarImporter):
def import_data(self, key: str, value: Any, obj: Any) -> None:
for ext in value:
version_spec = to_spec(ext['php_extension']['min_version'],
ext['php_extension']['max_version'])
extension, created = PhpExtension.objects.get_or_create(
id=ext['php_extension']['id'])
PhpExtensionDependency.objects.create(
max_version=none_to_empty_string(
ext['php_extension']['max_version']),
min_version=none_to_empty_string(
ext['php_extension']['min_version']),
version_spec=version_spec,
app_release=obj, php_extension=extension,
)


class DatabaseImporter(ScalarImporter):
def import_data(self, key: str, value: Any, obj: Any) -> None:
for db in value:
version_spec = to_spec(db['database']['min_version'],
db['database']['max_version'])
# all dbs should be known already
database = Database.objects.get(id=db['database']['id'])
DatabaseDependency.objects.create(
max_version=none_to_empty_string(
db['database']['max_version']),
min_version=none_to_empty_string(
db['database']['min_version']),
version_spec=version_spec,
app_release=obj, database=database,
)

Expand Down Expand Up @@ -97,6 +95,16 @@ def import_data(self, key: str, value: Any, obj: Any) -> None:
setattr(obj, key, none_to_empty_string(value))


class MinVersionImporter(ScalarImporter):
def import_data(self, key: str, value: Any, obj: Any) -> None:
setattr(obj, key, value)


class MaxVersionImporter(ScalarImporter):
def import_data(self, key: str, value: Any, obj: Any) -> None:
setattr(obj, key, value)


class ScreenshotsImporter(ScalarImporter):
def import_data(self, key: str, value: Any, obj: Any) -> None:
obj.screenshots = list(map(
Expand Down Expand Up @@ -136,15 +144,14 @@ def __init__(self, php_extension_importer: PhpExtensionImporter,
'php_extensions': php_extension_importer,
'databases': database_importer,
'licenses': license_importer,
'php_min_version': string_attribute_importer,
'php_max_version': string_attribute_importer,
'platform_min_version': string_attribute_importer,
'platform_max_version': string_attribute_importer,
'php_version_spec': string_attribute_importer,
'platform_version_spec': string_attribute_importer,
'min_int_size': integer_attribute_importer,
'shell_commands': shell_command_importer,
'checksum': string_attribute_importer,
'download': string_attribute_importer,
}, {'version'})
}, {'version', 'php_min_version', 'php_max_version',
'platform_min_version', 'platform_max_version'})

def _before_import(self, obj: Any) -> None:
obj.licenses.clear()
Expand All @@ -153,6 +160,14 @@ def _before_import(self, obj: Any) -> None:
obj.php_extensions.clear()
obj.databases.clear()

def import_data(self, key: str, value: Any, obj: Any) -> None:
# combine versions into specs
value['platform_version_spec'] = to_spec(
value['platform_min_version'], value['platform_max_version'])
value['php_version_spec'] = to_spec(value['php_min_version'],
value['php_max_version'])
super().import_data(key, value, obj)

def _get_object(self, value: Any, obj: Any) -> Any:
release, created = AppRelease.objects.get_or_create(
version=value['version'], app=obj
Expand Down
18 changes: 9 additions & 9 deletions nextcloudappstore/core/api/v1/release/info.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@

<!-- release -->
<release>
<version>
<version type="min-version">
<xsl:value-of select="version"/>
</version>
<licenses type="list">
Expand All @@ -102,10 +102,10 @@
</xsl:template>

<xsl:template match="dependencies">
<php-min-version>
<php-min-version type="min-version">
<xsl:value-of select="php/@min-version"/>
</php-min-version>
<php-max-version>
<php-max-version type="max-version">
<xsl:value-of select="php/@max-version"/>
</php-max-version>
<xsl:choose>
Expand All @@ -118,20 +118,20 @@
<min-int-size type="int">32</min-int-size>
</xsl:otherwise>
</xsl:choose>
<platform-min-version>
<platform-min-version type="min-version">
<xsl:value-of select="owncloud/@min-version"/>
</platform-min-version>
<platform-max-version>
<platform-max-version type="max-version">
<xsl:value-of select="owncloud/@max-version"/>
</platform-max-version>

<php-extensions type="list">
<xsl:for-each select="lib">
<php-extension>
<min-version>
<min-version type="min-version">
<xsl:value-of select="@min-version"/>
</min-version>
<max-version>
<max-version type="max-version">
<xsl:value-of select="@max-version"/>
</max-version>
<id>
Expand All @@ -143,10 +143,10 @@
<databases type="list">
<xsl:for-each select="database">
<database>
<min-version>
<min-version type="min-version">
<xsl:value-of select="@min-version"/>
</min-version>
<max-version>
<max-version type="max-version">
<xsl:value-of select="@max-version"/>
</max-version>
<id>
Expand Down
5 changes: 5 additions & 0 deletions nextcloudappstore/core/api/v1/release/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Dict, Any

from nextcloudappstore.core.api.v1.release import ReleaseConfig
from nextcloudappstore.core.versioning import pad_max_version, pad_min_version
from rest_framework.exceptions import APIException # type: ignore


Expand Down Expand Up @@ -91,6 +92,10 @@ def element_to_dict(element: Any) -> Dict:
return {key: int(element.text)}
elif type == 'list':
return {key: list(map(element_to_dict, element.iterchildren()))}
elif type == 'min-version':
return {key: pad_min_version(element.text)}
elif type == 'max-version':
return {key: pad_max_version(element.text)}
elif len(list(element)) > 0:
contents = {}
for child in element.iterchildren():
Expand Down
30 changes: 20 additions & 10 deletions nextcloudappstore/core/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@
from parler_rest.fields import TranslatedFieldsField
from parler_rest.serializers import TranslatableModelSerializer
from rest_framework import serializers
from rest_framework.fields import SerializerMethodField


class PhpExtensionDependencySerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(source='php_extension.id')
version_spec = SerializerMethodField()

class Meta:
model = PhpExtensionDependency
fields = ('id', 'min_version', 'max_version')
fields = ('id', 'version_spec')

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


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', 'min_version', 'max_version')

fields = ('id', 'name', 'version_spec')

class LicenseSerializer(serializers.ModelSerializer):
class Meta:
model = License
fields = ('id', 'name')
def get_version_spec(self, obj):
return obj.version_spec.replace(',', ' ')


class LicenseSerializer(serializers.ModelSerializer):
Expand All @@ -49,16 +52,23 @@ class AppReleaseSerializer(serializers.ModelSerializer):
PhpExtensionDependencySerializer(many=True, read_only=True,
source='phpextensiondependencies')
licenses = LicenseSerializer(many=True, read_only=True)
php_version_spec = SerializerMethodField()
platform_version_spec = SerializerMethodField()

class Meta:
model = AppRelease
fields = (
'version', 'php_extensions', 'databases', 'shell_commands',
'php_min_version', 'php_max_version', 'platform_min_version',
'platform_max_version', 'min_int_size', 'download', 'created',
'licenses', 'last_modified', 'checksum'
'php_version_spec', 'platform_version_spec', 'min_int_size',
'download', 'created', 'licenses', 'last_modified', 'checksum'
)

def get_platform_version_spec(self, obj):
return obj.platform_version_spec.replace(',', ' ')

def get_php_version_spec(self, obj):
return obj.php_version_spec.replace(',', ' ')


class ScreenshotSerializer(serializers.ModelSerializer):
class Meta:
Expand Down
12 changes: 5 additions & 7 deletions nextcloudappstore/core/api/v1/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_delete_not_found(self):
def test_releases_platform_min(self):
app = App.objects.create(pk='news', owner=self.user)
AppRelease.objects.create(app=app, version='10.1',
platform_min_version='9.1.1')
platform_version_spec='>=9.1.1')
url = reverse('api-v1:apps', kwargs={'version': '9.1.0'})
response = self.api_client.get(url)
self.assertEqual(200, response.status_code)
Expand All @@ -66,8 +66,7 @@ def test_releases_platform_min(self):
def test_releases_platform_min_max(self):
app = App.objects.create(pk='news', owner=self.user)
AppRelease.objects.create(app=app, version='10.1',
platform_min_version='9.1.1',
platform_max_version='9.1.1')
platform_version_spec='>=9.1.1,<9.1.2')
url = reverse('api-v1:apps', kwargs={'version': '9.1.2'})
response = self.api_client.get(url)
self.assertEqual(200, response.status_code)
Expand All @@ -76,7 +75,7 @@ def test_releases_platform_min_max(self):
def test_releases_platform_max(self):
app = App.objects.create(pk='news', owner=self.user)
AppRelease.objects.create(app=app, version='10.1',
platform_max_version='9.1.1')
platform_version_spec='<9.1.2')
url = reverse('api-v1:apps', kwargs={'version': '9.1.2'})
response = self.api_client.get(url)
self.assertEqual(200, response.status_code)
Expand All @@ -85,7 +84,7 @@ def test_releases_platform_max(self):
def test_releases_platform_max_wildcard(self):
app = App.objects.create(pk='news', owner=self.user)
AppRelease.objects.create(app=app, version='10.1',
platform_max_version='9.1')
platform_version_spec='<9.2.0')
url = reverse('api-v1:apps', kwargs={'version': '9.1.2'})
response = self.api_client.get(url)
self.assertEqual(200, response.status_code)
Expand All @@ -94,8 +93,7 @@ def test_releases_platform_max_wildcard(self):
def test_releases_platform_ok(self):
app = App.objects.create(pk='news', owner=self.user)
AppRelease.objects.create(app=app, version='10.1',
platform_max_version='9.1.1',
platform_min_version='9.1.1')
platform_version_spec='>=9.1.1,<9.1.2')
url = reverse('api-v1:apps', kwargs={'version': '9.1.1'})
response = self.api_client.get(url)
self.assertEqual(200, response.status_code)
Expand Down
8 changes: 7 additions & 1 deletion nextcloudappstore/core/api/v1/tests/test_app_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ class AppReleaseTest(ApiTest):
delete_url = reverse('api-v1:app-release-delete',
kwargs={'app': 'news', 'version': '9.0.0'})
create_url = reverse('api-v1:app-release-create')
app_args = {'app': {'id': 'news', 'release': {'version': '9.0.0'}}}
app_args = {'app': {'id': 'news', 'release': {
'version': '9.0.0',
'platform_min_version': '9.0.0',
'platform_max_version': '*',
'php_min_version': '5.6.0',
'php_max_version': '*',
}}}

def create_release(self, owner, version='9.0.0', co_maintainers=[]):
app = App.objects.create(id='news', owner=owner)
Expand Down

0 comments on commit 9d37161

Please sign in to comment.