Use doc strings for resource and method docs. Tweak some docs. Switch to 100 length lines.
This commit is contained in:
parent
978d68f0e0
commit
220649e579
3 changed files with 43 additions and 51 deletions
|
@ -56,10 +56,9 @@ def method_metadata(func, name):
|
||||||
nickname = partial(add_method_metadata, 'nickname')
|
nickname = partial(add_method_metadata, 'nickname')
|
||||||
|
|
||||||
|
|
||||||
def query_parameter(name, help_str, type=reqparse.text_type,
|
def query_param(name, help_str, type=reqparse.text_type, default=None,
|
||||||
default=None, choices=(), required=False):
|
choices=(), required=False):
|
||||||
def add_param(func):
|
def add_param(func):
|
||||||
logger.debug('%s', func)
|
|
||||||
if '__api_query_params' not in dir(func):
|
if '__api_query_params' not in dir(func):
|
||||||
func.__api_query_params = []
|
func.__api_query_params = []
|
||||||
func.__api_query_params.append({
|
func.__api_query_params.append({
|
||||||
|
@ -78,8 +77,6 @@ def parse_args(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
if '__api_query_params' not in dir(func):
|
if '__api_query_params' not in dir(func):
|
||||||
logger.debug('No query params defined.')
|
|
||||||
logger.debug('%s', func)
|
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
|
@ -139,9 +136,8 @@ def validate_json_request(schema_name):
|
||||||
|
|
||||||
def log_action(kind, user_or_orgname, metadata={}, repo=None):
|
def log_action(kind, user_or_orgname, metadata={}, repo=None):
|
||||||
performer = current_user.db_user()
|
performer = current_user.db_user()
|
||||||
model.log_action(kind, user_or_orgname, performer=performer,
|
model.log_action(kind, user_or_orgname, performer=performer, ip=request.remote_addr,
|
||||||
ip=request.remote_addr, metadata=metadata, repository=repo)
|
metadata=metadata, repository=repo)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import endpoints.api.legacy
|
import endpoints.api.legacy
|
||||||
|
|
|
@ -29,11 +29,12 @@ def swagger_route_data():
|
||||||
endpoint_method = app.view_functions[rule.endpoint]
|
endpoint_method = app.view_functions[rule.endpoint]
|
||||||
|
|
||||||
if 'view_class' in dir(endpoint_method):
|
if 'view_class' in dir(endpoint_method):
|
||||||
|
view_class = endpoint_method.view_class
|
||||||
operations = []
|
operations = []
|
||||||
|
|
||||||
method_names = list(rule.methods.difference(['HEAD', 'OPTIONS']))
|
method_names = list(rule.methods.difference(['HEAD', 'OPTIONS']))
|
||||||
for method_name in method_names:
|
for method_name in method_names:
|
||||||
method = getattr(endpoint_method.view_class, method_name.lower(), None)
|
method = getattr(view_class, method_name.lower(), None)
|
||||||
|
|
||||||
parameters = []
|
parameters = []
|
||||||
for param in rule.arguments:
|
for param in rule.arguments:
|
||||||
|
@ -49,16 +50,15 @@ def swagger_route_data():
|
||||||
if req_schema_name:
|
if req_schema_name:
|
||||||
parameters.append({
|
parameters.append({
|
||||||
'paramType': 'body',
|
'paramType': 'body',
|
||||||
'name': 'request_body',
|
'name': 'body',
|
||||||
'description': 'Request body contents.',
|
'description': 'Request body contents.',
|
||||||
'dataType': req_schema_name,
|
'dataType': req_schema_name,
|
||||||
'required': True,
|
'required': True,
|
||||||
})
|
})
|
||||||
|
|
||||||
schema = endpoint_method.view_class.schemas[req_schema_name]
|
schema = view_class.schemas[req_schema_name]
|
||||||
models[req_schema_name] = schema
|
models[req_schema_name] = schema
|
||||||
|
|
||||||
logger.debug('method dir: %s', dir(method))
|
|
||||||
if '__api_query_params' in dir(method):
|
if '__api_query_params' in dir(method):
|
||||||
for param_spec in method.__api_query_params:
|
for param_spec in method.__api_query_params:
|
||||||
new_param = {
|
new_param = {
|
||||||
|
@ -79,13 +79,14 @@ def swagger_route_data():
|
||||||
'method': method_name,
|
'method': method_name,
|
||||||
'nickname': method_metadata(method, 'nickname'),
|
'nickname': method_metadata(method, 'nickname'),
|
||||||
'type': 'void',
|
'type': 'void',
|
||||||
|
'summary': method.__doc__ if method.__doc__ else '',
|
||||||
'parameters': parameters,
|
'parameters': parameters,
|
||||||
})
|
})
|
||||||
|
|
||||||
swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
|
swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
|
||||||
apis.append({
|
apis.append({
|
||||||
'path': swagger_path,
|
'path': swagger_path,
|
||||||
'description': 'Resource description.',
|
'description': view_class.__doc__ if view_class.__doc__ else "",
|
||||||
'operations': operations,
|
'operations': operations,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -96,10 +97,9 @@ def swagger_route_data():
|
||||||
'resourcePath': '/',
|
'resourcePath': '/',
|
||||||
'info': {
|
'info': {
|
||||||
'title': 'Quay.io API',
|
'title': 'Quay.io API',
|
||||||
'description': ('This API allows you to perform many of the operations '
|
'description': ('This API allows you to perform many of the operations required to work '
|
||||||
'required to work with Quay.io repositories, users, and '
|
'with Quay.io repositories, users, and organizations. You can find out more '
|
||||||
'organizations. You can find out more at '
|
'at <a href="https://quay.io">Quay.io</a>.'),
|
||||||
'<a href="https://quay.io">Quay.io</a>.'),
|
|
||||||
'termsOfServiceUrl': 'https://quay.io/tos',
|
'termsOfServiceUrl': 'https://quay.io/tos',
|
||||||
'contact': 'support@quay.io',
|
'contact': 'support@quay.io',
|
||||||
},
|
},
|
||||||
|
@ -110,6 +110,8 @@ def swagger_route_data():
|
||||||
|
|
||||||
@resource('/v1/discovery')
|
@resource('/v1/discovery')
|
||||||
class DiscoveryResource(Resource):
|
class DiscoveryResource(Resource):
|
||||||
|
"""Ability to inspect the API for usage information and documentation."""
|
||||||
@nickname('discovery')
|
@nickname('discovery')
|
||||||
def get(self):
|
def get(self):
|
||||||
|
""" List all of the API endpoints available in the swagger API format."""
|
||||||
return swagger_route_data()
|
return swagger_route_data()
|
||||||
|
|
|
@ -5,12 +5,10 @@ from flask.ext.restful import Resource, reqparse, abort
|
||||||
from flask.ext.login import current_user
|
from flask.ext.login import current_user
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
from endpoints.api import (truthy_bool, format_date, nickname, log_action,
|
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
|
||||||
validate_json_request, require_repo_read,
|
require_repo_read, RepositoryParamResource, resource, query_param,
|
||||||
RepositoryParamResource, resource, query_parameter,
|
|
||||||
parse_args)
|
parse_args)
|
||||||
from auth.permissions import (ReadRepositoryPermission,
|
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
|
||||||
ModifyRepositoryPermission,
|
|
||||||
AdministerRepositoryPermission)
|
AdministerRepositoryPermission)
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,11 +17,12 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@resource('/v1/repository')
|
@resource('/v1/repository')
|
||||||
class RepositoryList(Resource):
|
class RepositoryList(Resource):
|
||||||
|
"""Operations for creating and listing repositories."""
|
||||||
schemas = {
|
schemas = {
|
||||||
'NewRepo': {
|
'NewRepo': {
|
||||||
'id': 'NewRepo',
|
'id': 'NewRepo',
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'description': 'Description of a new repository.',
|
'description': 'Description of a new repository',
|
||||||
'required': [
|
'required': [
|
||||||
'repository',
|
'repository',
|
||||||
'visibility',
|
'visibility',
|
||||||
|
@ -31,11 +30,11 @@ class RepositoryList(Resource):
|
||||||
'properties': {
|
'properties': {
|
||||||
'repository': {
|
'repository': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': 'Repository name.',
|
'description': 'Repository name',
|
||||||
},
|
},
|
||||||
'visibility': {
|
'visibility': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': 'Visibility which the repository will start with.',
|
'description': 'Visibility which the repository will start with',
|
||||||
'enum': [
|
'enum': [
|
||||||
'public',
|
'public',
|
||||||
'private',
|
'private',
|
||||||
|
@ -43,13 +42,12 @@ class RepositoryList(Resource):
|
||||||
},
|
},
|
||||||
'namespace': {
|
'namespace': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': ('Namespace in which the repository should be '
|
'description': ('Namespace in which the repository should be created. If omitted, the '
|
||||||
'created. If omitted, the username of the caller is'
|
'username of the caller is used'),
|
||||||
'used.'),
|
|
||||||
},
|
},
|
||||||
'description': {
|
'description': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': 'Markdown encoded description for the repository.',
|
'description': 'Markdown encoded description for the repository',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +56,7 @@ class RepositoryList(Resource):
|
||||||
@nickname('createRepo')
|
@nickname('createRepo')
|
||||||
@validate_json_request('NewRepo')
|
@validate_json_request('NewRepo')
|
||||||
def post(self):
|
def post(self):
|
||||||
|
"""Create a new repository."""
|
||||||
owner = current_user.db_user()
|
owner = current_user.db_user()
|
||||||
req = request.get_json()
|
req = request.get_json()
|
||||||
namespace_name = req['namespace'] if 'namespace' in req else owner.username
|
namespace_name = req['namespace'] if 'namespace' in req else owner.username
|
||||||
|
@ -78,9 +77,8 @@ class RepositoryList(Resource):
|
||||||
repo.description = req['description']
|
repo.description = req['description']
|
||||||
repo.save()
|
repo.save()
|
||||||
|
|
||||||
log_action('create_repo', namespace_name,
|
log_action('create_repo', namespace_name, {'repo': repository_name,
|
||||||
{'repo': repository_name, 'namespace': namespace_name},
|
'namespace': namespace_name}, repo=repo)
|
||||||
repo=repo)
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'namespace': namespace_name,
|
'namespace': namespace_name,
|
||||||
'name': repository_name
|
'name': repository_name
|
||||||
|
@ -90,20 +88,17 @@ class RepositoryList(Resource):
|
||||||
|
|
||||||
@nickname('listRepos')
|
@nickname('listRepos')
|
||||||
@parse_args
|
@parse_args
|
||||||
@query_parameter('page', 'Offset page number. (int)', type=int)
|
@query_param('page', 'Offset page number. (int)', type=int)
|
||||||
@query_parameter('limit', 'Limit on the number of results (int)', type=int)
|
@query_param('limit', 'Limit on the number of results (int)', type=int)
|
||||||
@query_parameter('namespace', ('Namespace to use when querying for org '
|
@query_param('namespace', 'Namespace to use when querying for org repositories.', type=str)
|
||||||
'repositories.'), type=str)
|
@query_param('public', 'Whether to include public repositories.', type=truthy_bool, default=True)
|
||||||
@query_parameter('public', 'Whether to include public repositories.',
|
@query_param('private', 'Whether to inlcude private repositories.', type=truthy_bool,
|
||||||
type=truthy_bool, default=True)
|
default=True)
|
||||||
@query_parameter('private', 'Whether to inlcude private repositories.',
|
@query_param('sort', 'Whether to sort the results.', type=truthy_bool, default=False)
|
||||||
type=truthy_bool, default=True)
|
@query_param('count', 'Whether to include a count of the total number of results available.',
|
||||||
@query_parameter('sort', 'Whether to sort the results.', type=truthy_bool,
|
type=truthy_bool, default=False)
|
||||||
default=False)
|
|
||||||
@query_parameter('count', ('Whether to include a count of the total number '
|
|
||||||
'of results available.'), type=truthy_bool,
|
|
||||||
default=False)
|
|
||||||
def get(self, args):
|
def get(self, args):
|
||||||
|
"""Fetch the list of repositories under a variety of situations."""
|
||||||
def repo_view(repo_obj):
|
def repo_view(repo_obj):
|
||||||
return {
|
return {
|
||||||
'namespace': repo_obj.namespace,
|
'namespace': repo_obj.namespace,
|
||||||
|
@ -120,15 +115,12 @@ class RepositoryList(Resource):
|
||||||
|
|
||||||
repo_count = None
|
repo_count = None
|
||||||
if args['count']:
|
if args['count']:
|
||||||
repo_count = model.get_visible_repository_count(username,
|
repo_count = model.get_visible_repository_count(username, include_public=args['public'],
|
||||||
include_public=args['public'],
|
|
||||||
namespace=args['namespace'])
|
namespace=args['namespace'])
|
||||||
response['count'] = repo_count
|
response['count'] = repo_count
|
||||||
|
|
||||||
repo_query = model.get_visible_repositories(username, limit=args['limit'],
|
repo_query = model.get_visible_repositories(username, limit=args['limit'], page=args['page'],
|
||||||
page=args['page'],
|
include_public=args['public'], sort=args['sort'],
|
||||||
include_public=args['public'],
|
|
||||||
sort=args['sort'],
|
|
||||||
namespace=args['namespace'])
|
namespace=args['namespace'])
|
||||||
|
|
||||||
response['repositories'] = [repo_view(repo) for repo in repo_query]
|
response['repositories'] = [repo_view(repo) for repo in repo_query]
|
||||||
|
@ -153,9 +145,11 @@ def image_view(image):
|
||||||
|
|
||||||
@resource('/v1/repository/<path:repository>')
|
@resource('/v1/repository/<path:repository>')
|
||||||
class Repository(RepositoryParamResource):
|
class Repository(RepositoryParamResource):
|
||||||
|
"""Operations for managing a specific repository."""
|
||||||
@require_repo_read
|
@require_repo_read
|
||||||
@nickname('getRepo')
|
@nickname('getRepo')
|
||||||
def get(self, namespace, repository):
|
def get(self, namespace, repository):
|
||||||
|
"""Fetch the specified repository."""
|
||||||
logger.debug('Get repo: %s/%s' % (namespace, repository))
|
logger.debug('Get repo: %s/%s' % (namespace, repository))
|
||||||
|
|
||||||
def tag_view(tag):
|
def tag_view(tag):
|
||||||
|
|
Reference in a new issue