Skip to content

Commit

Permalink
Remove accepts style decorator from catalog plugin (#14951)
Browse files Browse the repository at this point in the history
  • Loading branch information
Qubad786 authored Nov 20, 2024
1 parent ed1cf18 commit 94a389a
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 147 deletions.
2 changes: 2 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from .alert import * # noqa
from .alertservice import * # noqa
from .api_key import * # noqa
from .app import * # noqa
from .auth import * # noqa
from .boot_environments import * # noqa
from .catalog import * # noqa
from .cloud_sync import * # noqa
from .common import * # noqa
from .core import * # noqa
Expand Down
31 changes: 31 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from middlewared.api.base import BaseModel, NonEmptyString

from .catalog import CatalogAppInfo


__all__ = [
'AppCategoriesArgs', 'AppCategoriesResult', 'AppSimilarArgs', 'AppSimilarResult', 'AppAvailableResponse',
]


class AppAvailableResponse(CatalogAppInfo):
catalog: NonEmptyString
installed: bool
train: NonEmptyString


class AppCategoriesArgs(BaseModel):
pass


class AppCategoriesResult(BaseModel):
result: list[NonEmptyString]


class AppSimilarArgs(BaseModel):
app_name: NonEmptyString
train: NonEmptyString


class AppSimilarResult(BaseModel):
result: list[AppAvailableResponse]
123 changes: 123 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from datetime import datetime

from pydantic import ConfigDict, Field, RootModel

from middlewared.api.base import BaseModel, ForUpdateMetaclass, NonEmptyString, single_argument_args, LongString


__all__ = [
'CatalogEntry', 'CatalogUpdateArgs', 'CatalogUpdateResult', 'CatalogTrainsArgs', 'CatalogTrainsResult',
'CatalogSyncArgs', 'CatalogSyncResult', 'CatalogAppInfo', 'CatalogAppsArgs', 'CatalogAppsResult',
'CatalogAppDetailsArgs', 'CatalogAppDetailsResult',
]


class CatalogEntry(BaseModel):
id: NonEmptyString
label: NonEmptyString = Field(pattern=r'^\w+[\w.-]*$')
preferred_trains: list[NonEmptyString]


@single_argument_args('catalog_update')
class CatalogUpdateArgs(BaseModel, metaclass=ForUpdateMetaclass):
preferred_trains: list[NonEmptyString]


class CatalogUpdateResult(BaseModel):
result: CatalogEntry


class CatalogTrainsArgs(BaseModel):
pass


class CatalogTrainsResult(BaseModel):
result: list[NonEmptyString]


class CatalogSyncArgs(BaseModel):
pass


class CatalogSyncResult(BaseModel):
result: None


class Maintainer(BaseModel):
name: str
email: str
url: str | None


class CatalogAppInfo(BaseModel):
app_readme: LongString | None
'''HTML content of the app README.'''
categories: list[str]
'''List of categories for the app.'''
description: str
'''Short description of the app.'''
healthy: bool
'''Health status of the app.'''
healthy_error: str | None = None
'''Error if app is not healthy.'''
home: str
'''Homepage URL of the app.'''
location: str
'''Local path to the app's location.'''
latest_version: str | None
'''Latest available app version.'''
latest_app_version: str | None
'''Latest available app version in repository.'''
latest_human_version: str | None
'''Human-readable version of the app.'''
last_update: datetime | None
'''Timestamp of the last update in ISO format.'''
name: str
'''Name of the app.'''
recommended: bool
'''Indicates if the app is recommended.'''
title: str
'''Title of the app.'''
maintainers: list[Maintainer]
'''List of app maintainers.'''
tags: list[str]
'''Tags associated with the app.'''
screenshots: list[str]
'''List of screenshot URLs.'''
sources: list[str]
'''List of source URLs.'''
icon_url: str | None = None
'''URL of the app icon'''

# We do this because if we change anything in catalog.json, even older releases will
# get this new field and different roles will start breaking due to this
model_config = ConfigDict(extra='allow')


@single_argument_args('catalog_apps_options')
class CatalogAppsArgs(BaseModel):
cache: bool = True
cache_only: bool = False
retrieve_all_trains: bool = True
trains: list[NonEmptyString] = Field(default_factory=list)


class CatalogTrainInfo(RootModel[dict[str, CatalogAppInfo]]):
pass


class CatalogAppsResult(BaseModel):
result: dict[str, CatalogTrainInfo]


class CatalogAppVersionDetails(BaseModel):
train: NonEmptyString


class CatalogAppDetailsArgs(BaseModel):
app_name: NonEmptyString
app_version_details: CatalogAppVersionDetails


class CatalogAppDetailsResult(BaseModel):
result: CatalogAppInfo
35 changes: 3 additions & 32 deletions src/middlewared/middlewared/plugins/catalog/app_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from catalog_reader.train_utils import get_train_path

from middlewared.schema import accepts, Bool, Dict, List, returns, Str
from middlewared.api import api_method
from middlewared.api.current import CatalogAppDetailsArgs, CatalogAppDetailsResult
from middlewared.service import CallError, Service

from .apps_util import get_app_details
Expand All @@ -15,37 +16,7 @@ class CatalogService(Service):
class Config:
cli_namespace = 'app.catalog'

@accepts(
Str('app_name'),
Dict(
'app_version_details',
Str('train', required=True),
),
roles=['CATALOG_READ'],
)
@returns(Dict(
# TODO: Make sure keys here are mapped appropriately
'app_details',
Str('name', required=True),
List('categories', items=[Str('category')], required=True),
List('maintainers', required=True),
List('tags', required=True),
List('screenshots', required=True, items=[Str('screenshot')]),
List('sources', required=True, items=[Str('source')]),
Str('app_readme', null=True, required=True),
Str('location', required=True),
Bool('healthy', required=True),
Bool('recommended', required=True),
Str('healthy_error', required=True, null=True),
Str('healthy_error', required=True, null=True),
Dict('versions', required=True, additional_attrs=True),
Str('latest_version', required=True, null=True),
Str('latest_app_version', required=True, null=True),
Str('latest_human_version', required=True, null=True),
Str('last_update', required=True, null=True),
Str('icon_url', required=True, null=True),
Str('home', required=True),
))
@api_method(CatalogAppDetailsArgs, CatalogAppDetailsResult, roles=['CATALOG_READ'])
def get_app_details(self, app_name, options):
"""
Retrieve information of `app_name` `app_version_details.catalog` catalog app.
Expand Down
52 changes: 11 additions & 41 deletions src/middlewared/middlewared/plugins/catalog/apps.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from middlewared.schema import accepts, Bool, Datetime, Dict, List, Ref, returns, Str
from middlewared.service import filterable, filterable_returns, Service
from middlewared.api import api_method
from middlewared.api.current import (
AppCategoriesArgs, AppCategoriesResult, AppAvailableResponse,
)
from middlewared.api.v25_04_0 import AppSimilarArgs, AppSimilarResult
from middlewared.service import filterable_api_method, Service
from middlewared.utils import filter_list


Expand All @@ -8,8 +12,7 @@ class AppService(Service):
class Config:
cli_namespace = 'app'

@filterable(roles=['CATALOG_READ'])
@filterable_returns(Ref('available_apps'))
@filterable_api_method(item=AppAvailableResponse, roles=['CATALOG_READ'])
async def latest(self, filters, options):
"""
Retrieve latest updated apps.
Expand All @@ -22,38 +25,7 @@ async def latest(self, filters, options):
), filters, options
)

@filterable(roles=['CATALOG_READ'])
@filterable_returns(Dict(
'available_apps',
Bool('healthy', required=True),
Bool('installed', required=True),
Bool('recommended', required=True),
Datetime('last_update', required=True),
List('capabilities', required=True),
List('run_as_context', required=True),
List('categories', required=True),
List('maintainers', required=True),
List('tags', required=True),
List('screenshots', required=True, items=[Str('screenshot')]),
List('sources', required=True, items=[Str('source')]),
Str('name', required=True),
Str('title', required=True),
Str('description', required=True),
Str('app_readme', required=True),
Str('location', required=True),
Str('healthy_error', required=True, null=True),
Str('home', required=True),
Str('latest_version', required=True),
Str('latest_app_version', required=True),
Str('latest_human_version', required=True),
Str('icon_url', null=True, required=True),
Str('train', required=True),
Str('catalog', required=True),
register=True,
# We do this because if we change anything in catalog.json, even older releases will
# get this new field and different roles will start breaking due to this
additional_attrs=True,
))
@filterable_api_method(item=AppAvailableResponse, roles=['CATALOG_READ'])
def available(self, filters, options):
"""
Retrieve all available applications from all configured catalogs.
Expand All @@ -68,7 +40,7 @@ def available(self, filters, options):
]

catalog = self.middleware.call_sync('catalog.config')
for train, train_data in self.middleware.call_sync('catalog.apps').items():
for train, train_data in self.middleware.call_sync('catalog.apps', {}).items():
if train not in catalog['preferred_trains']:
continue

Expand All @@ -82,16 +54,14 @@ def available(self, filters, options):

return filter_list(results, filters, options)

@accepts(roles=['CATALOG_READ'])
@returns(List(items=[Str('category')]))
@api_method(AppCategoriesArgs, AppCategoriesResult, roles=['CATALOG_READ'])
async def categories(self):
"""
Retrieve list of valid categories which have associated applications.
"""
return sorted(list(await self.middleware.call('catalog.retrieve_mapped_categories')))

@accepts(Str('app_name'), Str('train'), roles=['CATALOG_READ'])
@returns(List(items=[Ref('available_apps')]))
@api_method(AppSimilarArgs, AppSimilarResult, roles=['CATALOG_READ'])
def similar(self, app_name, train):
"""
Retrieve applications which are similar to `app_name`.
Expand Down
45 changes: 3 additions & 42 deletions src/middlewared/middlewared/plugins/catalog/apps_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from datetime import datetime
from jsonschema import validate as json_schema_validate, ValidationError as JsonValidationError

from middlewared.schema import accepts, Bool, Dict, List, returns, Str
from middlewared.api import api_method
from middlewared.api.current import CatalogAppsArgs, CatalogAppsResult
from middlewared.service import private, Service

from .apps_util import get_app_version_details
Expand Down Expand Up @@ -45,47 +46,7 @@ def train_to_apps_version_mapping(self):
def cached(self, label):
return self.middleware.call_sync('cache.has_key', get_cache_key(label))

@accepts(
Dict(
'options',
Bool('cache', default=True),
Bool('cache_only', default=False),
Bool('retrieve_all_trains', default=True),
List('trains', items=[Str('train_name')]),
),
roles=['CATALOG_READ']
)
@returns(Dict(
'trains',
additional_attrs=True,
example={
'stable': {
'plex': {
'app_readme': '<h1>Plex</h1>',
'categories': ['media'],
'description': 'Plex is a media server that allows you to stream your media to any Plex client.',
'healthy': True,
'healthy_error': None,
'home': 'https://plex.tv',
'location': '/mnt/.ix-apps/truenas_catalog/stable/plex',
'latest_version': '1.0.0',
'latest_app_version': '1.40.2.8395',
'latest_human_version': '1.40.2.8395_1.0.0',
'last_update': '2024-07-30 13:40:47+00:00',
'name': 'plex',
'recommended': False,
'title': 'Plex',
'maintainers': [
{'email': '[email protected]', 'name': 'truenas', 'url': 'https://www.truenas.com/'},
],
'tags': ['plex', 'media', 'entertainment', 'movies', 'series', 'tv', 'streaming'],
'screenshots': ['https://media.sys.truenas.net/apps/plex/screenshots/screenshot2.png'],
'sources': ['https://plex.tv', 'https://hub.docker.com/r/plexinc/pms-docker'],
'icon_url': 'https://media.sys.truenas.net/apps/plex/icons/icon.png'
},
},
}
))
@api_method(CatalogAppsArgs, CatalogAppsResult, roles=['CATALOG_READ'])
def apps(self, options):
"""
Retrieve apps details for `label` catalog.
Expand Down
8 changes: 4 additions & 4 deletions src/middlewared/middlewared/plugins/catalog/sync.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from middlewared.schema import accepts
from middlewared.service import job, private, returns, Service
from middlewared.api import api_method
from middlewared.api.current import CatalogSyncArgs, CatalogSyncResult
from middlewared.service import job, private, Service

from .git_utils import pull_clone_repository
from .utils import OFFICIAL_LABEL, OFFICIAL_CATALOG_REPO, OFFICIAL_CATALOG_BRANCH
Expand All @@ -13,8 +14,7 @@ class CatalogService(Service):
async def synced(self):
return self.SYNCED

@accepts(roles=['CATALOG_WRITE'])
@returns()
@api_method(CatalogSyncArgs, CatalogSyncResult, roles=['CATALOG_WRITE'])
@job(lock='official_catalog_sync')
async def sync(self, job):
"""
Expand Down
Loading

0 comments on commit 94a389a

Please sign in to comment.