diff --git a/application.py b/application.py
index 3d962e6ce..91062d4f6 100644
--- a/application.py
+++ b/application.py
@@ -9,7 +9,7 @@ application.config['LOGGING_CONFIG']()
# Turn off debug logging for boto
logging.getLogger('boto').setLevel(logging.CRITICAL)
-from endpoints.api import api
+from endpoints.api import api_bp
from endpoints.index import index
from endpoints.web import web
from endpoints.tags import tags
@@ -26,7 +26,7 @@ application.register_blueprint(callback, url_prefix='/oauth2')
application.register_blueprint(index, url_prefix='/v1')
application.register_blueprint(tags, url_prefix='/v1')
application.register_blueprint(registry, url_prefix='/v1')
-application.register_blueprint(api, url_prefix='/api')
+application.register_blueprint(api_bp, url_prefix='/api')
application.register_blueprint(webhooks, url_prefix='/webhooks')
application.register_blueprint(realtime, url_prefix='/realtime')
diff --git a/endpoints/api/__init__.py b/endpoints/api/__init__.py
index 2116018a1..16e6dcdb9 100644
--- a/endpoints/api/__init__.py
+++ b/endpoints/api/__init__.py
@@ -1,5 +1,8 @@
+import logging
+
from flask import Blueprint, request
-from flask.ext.restful import Resource, abort
+from flask.ext.restful import Resource, abort, Api, reqparse
+from flask.ext.restful.utils.cors import crossdomain
from flask.ext.login import current_user
from calendar import timegm
from email.utils import formatdate
@@ -13,8 +16,17 @@ from auth.permissions import (ReadRepositoryPermission,
AdministerRepositoryPermission)
+logger = logging.getLogger(__name__)
+api_bp = Blueprint('api', __name__)
+api = Api(api_bp)
+api.decorators = [crossdomain(origin='*')]
-api = Blueprint('api', __name__)
+
+def resource(*urls, **kwargs):
+ def wrapper(api_resource):
+ api.add_resource(api_resource, *urls, **kwargs)
+ return api_resource
+ return wrapper
def truthy_bool(param):
@@ -44,6 +56,41 @@ def method_metadata(func, name):
nickname = partial(add_method_metadata, 'nickname')
+def query_parameter(name, help_str, type=reqparse.text_type,
+ default=None, choices=(), required=False):
+ def add_param(func):
+ logger.debug('%s', func)
+ if '__api_query_params' not in dir(func):
+ func.__api_query_params = []
+ func.__api_query_params.append({
+ 'name': name,
+ 'type': type,
+ 'help': help_str,
+ 'default': default,
+ 'choices': choices,
+ 'required': required,
+ })
+ return func
+ return add_param
+
+
+def parse_args(func):
+ @wraps(func)
+ def wrapper(self, *args, **kwargs):
+ if '__api_query_params' not in dir(func):
+ logger.debug('No query params defined.')
+ logger.debug('%s', func)
+ abort(400)
+
+ parser = reqparse.RequestParser()
+ for arg_spec in func.__api_query_params:
+ parser.add_argument(**arg_spec)
+ parsed_args = parser.parse_args()
+
+ return func(self, parsed_args, *args, **kwargs)
+ return wrapper
+
+
def parse_repository_name(func):
@wraps(func)
def wrapper(repository, *args, **kwargs):
diff --git a/endpoints/api/discovery.py b/endpoints/api/discovery.py
index a1bf03980..081dc05c0 100644
--- a/endpoints/api/discovery.py
+++ b/endpoints/api/discovery.py
@@ -1,17 +1,26 @@
import re
+import logging
-from flask.ext.restful import Api, Resource
-from flask.ext.restful.utils.cors import crossdomain
+from flask.ext.restful import Resource, reqparse
-from endpoints.api import api, method_metadata, nickname
-from endpoints.common import get_route_data
+from endpoints.api import resource, method_metadata, nickname, truthy_bool
from app import app
-discovery_api = Api(api)
-discovery_api.decorators = [crossdomain(origin='*')]
+logger = logging.getLogger(__name__)
+
+
+PARAM_REGEX = re.compile(r'<([\w]+:)?([\w]+)>')
+
+
+TYPE_CONVERTER = {
+ truthy_bool: 'boolean',
+ str: 'string',
+ basestring: 'string',
+ reqparse.text_type: 'string',
+ int: 'integer',
+}
-param_regex = re.compile(r'<([\w]+:)?([\w]+)>')
def swagger_route_data():
apis = []
@@ -49,6 +58,22 @@ def swagger_route_data():
schema = endpoint_method.view_class.schemas[req_schema_name]
models[req_schema_name] = schema
+ logger.debug('method dir: %s', dir(method))
+ if '__api_query_params' in dir(method):
+ for param_spec in method.__api_query_params:
+ new_param = {
+ 'paramType': 'query',
+ 'name': param_spec['name'],
+ 'description': param_spec['help'],
+ 'dataType': TYPE_CONVERTER[param_spec['type']],
+ 'required': param_spec['required'],
+ }
+
+ if len(param_spec['choices']) > 0:
+ new_param['enum'] = list(param_spec['choices'])
+
+ parameters.append(new_param)
+
if method is not None:
operations.append({
'method': method_name,
@@ -57,7 +82,7 @@ def swagger_route_data():
'parameters': parameters,
})
- swagger_path = param_regex.sub(r'{\2}', rule.rule)
+ swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
apis.append({
'path': swagger_path,
'description': 'Resource description.',
@@ -67,18 +92,24 @@ def swagger_route_data():
swagger_data = {
'apiVersion': 'v1',
'swaggerVersion': '1.2',
- 'basePath': 'https://quay.io/',
+ 'basePath': 'http://ci.devtable.com:5000',
'resourcePath': '/',
+ 'info': {
+ 'title': 'Quay.io API',
+ 'description': ('This API allows you to perform many of the operations '
+ 'required to work with Quay.io repositories, users, and '
+ 'organizations. You can find out more at '
+ 'Quay.io.'),
+ 'termsOfServiceUrl': 'https://quay.io/tos',
+ 'contact': 'support@quay.io',
+ },
'apis': apis,
'models': models,
}
return swagger_data
-
+@resource('/v1/discovery')
class DiscoveryResource(Resource):
@nickname('discovery')
def get(self):
return swagger_route_data()
-
-
-discovery_api.add_resource(DiscoveryResource, '/v1/discovery')
\ No newline at end of file
diff --git a/endpoints/api/legacy.py b/endpoints/api/legacy.py
index b776330d5..b03de7006 100644
--- a/endpoints/api/legacy.py
+++ b/endpoints/api/legacy.py
@@ -11,7 +11,7 @@ from functools import wraps
from collections import defaultdict
from urllib import quote
-from endpoints.api import api
+from endpoints.api import api_bp
from data import model
from data.plans import PLANS, get_plan
from app import app
@@ -43,7 +43,7 @@ build_logs = app.config['BUILDLOGS']
logger = logging.getLogger(__name__)
-@api.before_request
+@api_bp.before_request
def csrf_protect():
if request.method != "GET" and request.method != "HEAD":
token = session.get('_csrf_token', None)
@@ -111,18 +111,18 @@ def org_api_call(user_call_name):
return internal_decorator
-@api.route('/discovery')
+@api_bp.route('/discovery')
def discovery():
return jsonify(get_route_data())
-@api.route('/')
+@api_bp.route('/')
@internal_api_call
def welcome():
return jsonify({'version': '0.5'})
-@api.route('/plans/')
+@api_bp.route('/plans/')
def list_plans():
return jsonify({
'plans': PLANS,
@@ -165,7 +165,7 @@ def user_view(user):
}
-@api.route('/user/', methods=['GET'])
+@api_bp.route('/user/', methods=['GET'])
@internal_api_call
def get_logged_in_user():
if current_user.is_anonymous():
@@ -178,7 +178,7 @@ def get_logged_in_user():
return jsonify(user_view(user))
-@api.route('/user/private', methods=['GET'])
+@api_bp.route('/user/private', methods=['GET'])
@api_login_required
@internal_api_call
def get_user_private_allowed():
@@ -199,7 +199,7 @@ def get_user_private_allowed():
})
-@api.route('/user/convert', methods=['POST'])
+@api_bp.route('/user/convert', methods=['POST'])
@api_login_required
@internal_api_call
def convert_user_to_organization():
@@ -230,7 +230,7 @@ def convert_user_to_organization():
return conduct_signin(admin_username, admin_password)
-@api.route('/user/', methods=['PUT'])
+@api_bp.route('/user/', methods=['PUT'])
@api_login_required
@internal_api_call
def change_user_details():
@@ -264,7 +264,7 @@ def change_user_details():
return jsonify(user_view(user))
-@api.route('/user/', methods=['POST'])
+@api_bp.route('/user/', methods=['POST'])
@internal_api_call
def create_new_user():
user_data = request.get_json()
@@ -283,7 +283,7 @@ def create_new_user():
return request_error(exception=ex)
-@api.route('/signin', methods=['POST'])
+@api_bp.route('/signin', methods=['POST'])
@internal_api_call
def signin_user():
signin_data = request.get_json()
@@ -318,7 +318,7 @@ def conduct_signin(username_or_email, password):
return response
-@api.route("/signout", methods=['POST'])
+@api_bp.route("/signout", methods=['POST'])
@api_login_required
@internal_api_call
def logout():
@@ -327,7 +327,7 @@ def logout():
return jsonify({'success': True})
-@api.route("/recovery", methods=['POST'])
+@api_bp.route("/recovery", methods=['POST'])
@internal_api_call
def request_recovery_email():
email = request.get_json()['email']
@@ -336,7 +336,7 @@ def request_recovery_email():
return make_response('Created', 201)
-@api.route('/users/', methods=['GET'])
+@api_bp.route('/users/', methods=['GET'])
@api_login_required
def get_matching_users(prefix):
users = model.get_matching_users(prefix)
@@ -346,7 +346,7 @@ def get_matching_users(prefix):
})
-@api.route('/entities/', methods=['GET'])
+@api_bp.route('/entities/', methods=['GET'])
@api_login_required
def get_matching_entities(prefix):
teams = []
@@ -413,7 +413,7 @@ def team_view(orgname, team):
}
-@api.route('/organization/', methods=['POST'])
+@api_bp.route('/organization/', methods=['POST'])
@api_login_required
@internal_api_call
def create_organization():
@@ -460,7 +460,7 @@ def org_view(o, teams):
return view
-@api.route('/organization/', methods=['GET'])
+@api_bp.route('/organization/', methods=['GET'])
@api_login_required
def get_organization(orgname):
permission = OrganizationMemberPermission(orgname)
@@ -476,7 +476,7 @@ def get_organization(orgname):
abort(403)
-@api.route('/organization/', methods=['PUT'])
+@api_bp.route('/organization/', methods=['PUT'])
@api_login_required
@org_api_call('change_user_details')
def change_organization_details(orgname):
@@ -529,7 +529,7 @@ def prototype_view(proto, org_members):
'id': proto.uuid,
}
-@api.route('/organization//prototypes', methods=['GET'])
+@api_bp.route('/organization//prototypes', methods=['GET'])
@api_login_required
def get_organization_prototype_permissions(orgname):
permission = AdministerOrganizationPermission(orgname)
@@ -567,7 +567,7 @@ def log_prototype_action(action_kind, orgname, prototype, **kwargs):
log_action(action_kind, orgname, log_params)
-@api.route('/organization//prototypes', methods=['POST'])
+@api_bp.route('/organization//prototypes', methods=['POST'])
@api_login_required
def create_organization_prototype_permission(orgname):
permission = AdministerOrganizationPermission(orgname)
@@ -615,7 +615,7 @@ def create_organization_prototype_permission(orgname):
abort(403)
-@api.route('/organization//prototypes/',
+@api_bp.route('/organization//prototypes/',
methods=['DELETE'])
@api_login_required
def delete_organization_prototype_permission(orgname, prototypeid):
@@ -637,7 +637,7 @@ def delete_organization_prototype_permission(orgname, prototypeid):
abort(403)
-@api.route('/organization//prototypes/',
+@api_bp.route('/organization//prototypes/',
methods=['PUT'])
@api_login_required
def update_organization_prototype_permission(orgname, prototypeid):
@@ -666,7 +666,7 @@ def update_organization_prototype_permission(orgname, prototypeid):
abort(403)
-@api.route('/organization//members', methods=['GET'])
+@api_bp.route('/organization//members', methods=['GET'])
@api_login_required
def get_organization_members(orgname):
permission = AdministerOrganizationPermission(orgname)
@@ -695,7 +695,7 @@ def get_organization_members(orgname):
abort(403)
-@api.route('/organization//members/', methods=['GET'])
+@api_bp.route('/organization//members/', methods=['GET'])
@api_login_required
def get_organization_member(orgname, membername):
permission = AdministerOrganizationPermission(orgname)
@@ -724,7 +724,7 @@ def get_organization_member(orgname, membername):
abort(403)
-@api.route('/organization//private', methods=['GET'])
+@api_bp.route('/organization//private', methods=['GET'])
@api_login_required
@internal_api_call
@org_api_call('get_user_private_allowed')
@@ -764,7 +764,7 @@ def member_view(member):
}
-@api.route('/organization//team/',
+@api_bp.route('/organization//team/',
methods=['PUT', 'POST'])
@api_login_required
def update_organization_team(orgname, teamname):
@@ -810,7 +810,7 @@ def update_organization_team(orgname, teamname):
abort(403)
-@api.route('/organization//team/',
+@api_bp.route('/organization//team/',
methods=['DELETE'])
@api_login_required
def delete_organization_team(orgname, teamname):
@@ -823,7 +823,7 @@ def delete_organization_team(orgname, teamname):
abort(403)
-@api.route('/organization//team//members',
+@api_bp.route('/organization//team//members',
methods=['GET'])
@api_login_required
def get_organization_team_members(orgname, teamname):
@@ -846,7 +846,7 @@ def get_organization_team_members(orgname, teamname):
abort(403)
-@api.route('/organization//team//members/',
+@api_bp.route('/organization//team//members/',
methods=['PUT', 'POST'])
@api_login_required
def update_organization_team_member(orgname, teamname, membername):
@@ -875,7 +875,7 @@ def update_organization_team_member(orgname, teamname, membername):
abort(403)
-@api.route('/organization//team//members/',
+@api_bp.route('/organization//team//members/',
methods=['DELETE'])
@api_login_required
def delete_organization_team_member(orgname, teamname, membername):
@@ -891,7 +891,7 @@ def delete_organization_team_member(orgname, teamname, membername):
abort(403)
-@api.route('/repository', methods=['POST'])
+@api_bp.route('/repository', methods=['POST'])
@api_login_required
def create_repo():
owner = current_user.db_user()
@@ -925,7 +925,7 @@ def create_repo():
abort(403)
-@api.route('/find/repository', methods=['GET'])
+@api_bp.route('/find/repository', methods=['GET'])
def find_repos():
prefix = request.args.get('query', '')
@@ -948,7 +948,7 @@ def find_repos():
return jsonify(response)
-@api.route('/repository/', methods=['GET'])
+@api_bp.route('/repository/', methods=['GET'])
def list_repos():
def repo_view(repo_obj):
return {
@@ -1003,7 +1003,7 @@ def list_repos():
return jsonify(response)
-@api.route('/repository/', methods=['PUT'])
+@api_bp.route('/repository/', methods=['PUT'])
@api_login_required
@parse_repository_name
def update_repo(namespace, repository):
@@ -1025,7 +1025,7 @@ def update_repo(namespace, repository):
abort(403)
-@api.route('/repository//changevisibility',
+@api_bp.route('/repository//changevisibility',
methods=['POST'])
@api_login_required
@parse_repository_name
@@ -1046,7 +1046,7 @@ def change_repo_visibility(namespace, repository):
abort(403)
-@api.route('/repository/', methods=['DELETE'])
+@api_bp.route('/repository/', methods=['DELETE'])
@api_login_required
@parse_repository_name
def delete_repository(namespace, repository):
@@ -1077,7 +1077,7 @@ def image_view(image):
}
-@api.route('/repository/', methods=['GET'])
+@api_bp.route('/repository/', methods=['GET'])
@parse_repository_name
def get_repo(namespace, repository):
logger.debug('Get repo: %s/%s' % (namespace, repository))
@@ -1159,7 +1159,7 @@ def build_status_view(build_obj, can_write=False):
}
-@api.route('/repository//build/', methods=['GET'])
+@api_bp.route('/repository//build/', methods=['GET'])
@parse_repository_name
def get_repo_builds(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@@ -1176,7 +1176,7 @@ def get_repo_builds(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//build//status',
+@api_bp.route('/repository//build//status',
methods=['GET'])
@parse_repository_name
def get_repo_build_status(namespace, repository, build_uuid):
@@ -1193,7 +1193,7 @@ def get_repo_build_status(namespace, repository, build_uuid):
abort(403) # Permission denied
-@api.route('/repository//build//archiveurl',
+@api_bp.route('/repository//build//archiveurl',
methods=['GET'])
@parse_repository_name
def get_repo_build_archive_url(namespace, repository, build_uuid):
@@ -1211,7 +1211,7 @@ def get_repo_build_archive_url(namespace, repository, build_uuid):
abort(403) # Permission denied
-@api.route('/repository//build//logs',
+@api_bp.route('/repository//build//logs',
methods=['GET'])
@parse_repository_name
def get_repo_build_logs(namespace, repository, build_uuid):
@@ -1236,7 +1236,7 @@ def get_repo_build_logs(namespace, repository, build_uuid):
abort(403) # Permission denied
-@api.route('/repository//build/', methods=['POST'])
+@api_bp.route('/repository//build/', methods=['POST'])
@api_login_required
@parse_repository_name
def request_repo_build(namespace, repository):
@@ -1266,7 +1266,7 @@ def request_repo_build(namespace, repository):
resp = jsonify(build_status_view(build_request, True))
repo_string = '%s/%s' % (namespace, repository)
- resp.headers['Location'] = url_for('api.get_repo_build_status',
+ resp.headers['Location'] = url_for('api_bp.get_repo_build_status',
repository=repo_string,
build_uuid=build_request.uuid)
resp.status_code = 201
@@ -1282,7 +1282,7 @@ def webhook_view(webhook):
}
-@api.route('/repository//webhook/', methods=['POST'])
+@api_bp.route('/repository//webhook/', methods=['POST'])
@api_login_required
@parse_repository_name
def create_webhook(namespace, repository):
@@ -1292,7 +1292,7 @@ def create_webhook(namespace, repository):
webhook = model.create_webhook(repo, request.get_json())
resp = jsonify(webhook_view(webhook))
repo_string = '%s/%s' % (namespace, repository)
- resp.headers['Location'] = url_for('api.get_webhook',
+ resp.headers['Location'] = url_for('api_bp.get_webhook',
repository=repo_string,
public_id=webhook.public_id)
log_action('add_repo_webhook', namespace,
@@ -1303,7 +1303,7 @@ def create_webhook(namespace, repository):
abort(403) # Permissions denied
-@api.route('/repository//webhook/',
+@api_bp.route('/repository//webhook/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1320,7 +1320,7 @@ def get_webhook(namespace, repository, public_id):
abort(403) # Permission denied
-@api.route('/repository//webhook/', methods=['GET'])
+@api_bp.route('/repository//webhook/', methods=['GET'])
@api_login_required
@parse_repository_name
def list_webhooks(namespace, repository):
@@ -1334,7 +1334,7 @@ def list_webhooks(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//webhook/',
+@api_bp.route('/repository//webhook/',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@@ -1350,7 +1350,7 @@ def delete_webhook(namespace, repository, public_id):
abort(403) # Permission denied
-@api.route('/repository//trigger/',
+@api_bp.route('/repository//trigger/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1372,7 +1372,7 @@ def _prepare_webhook_url(scheme, username, password, hostname, path):
return urlparse.urlunparse((scheme, auth_hostname, path, '', '', ''))
-@api.route('/repository//trigger//subdir',
+@api_bp.route('/repository//trigger//subdir',
methods=['POST'])
@api_login_required
@parse_repository_name
@@ -1405,7 +1405,7 @@ def list_build_trigger_subdirs(namespace, repository, trigger_uuid):
abort(403) # Permission denied
-@api.route('/repository//trigger//activate',
+@api_bp.route('/repository//trigger//activate',
methods=['POST'])
@api_login_required
@parse_repository_name
@@ -1464,7 +1464,7 @@ def activate_build_trigger(namespace, repository, trigger_uuid):
abort(403) # Permission denied
-@api.route('/repository//trigger//start',
+@api_bp.route('/repository//trigger//start',
methods=['POST'])
@api_login_required
@parse_repository_name
@@ -1493,7 +1493,7 @@ def manually_start_build_trigger(namespace, repository, trigger_uuid):
resp = jsonify(build_status_view(build_request, True))
repo_string = '%s/%s' % (namespace, repository)
- resp.headers['Location'] = url_for('api.get_repo_build_status',
+ resp.headers['Location'] = url_for('api_bp.get_repo_build_status',
repository=repo_string,
build_uuid=build_request.uuid)
resp.status_code = 201
@@ -1502,7 +1502,7 @@ def manually_start_build_trigger(namespace, repository, trigger_uuid):
abort(403) # Permission denied
-@api.route('/repository//trigger//builds',
+@api_bp.route('/repository//trigger//builds',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1519,7 +1519,7 @@ def list_trigger_recent_builds(namespace, repository, trigger_uuid):
abort(403) # Permission denied
-@api.route('/repository//trigger//sources',
+@api_bp.route('/repository//trigger//sources',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1543,7 +1543,7 @@ def list_trigger_build_sources(namespace, repository, trigger_uuid):
-@api.route('/repository//trigger/', methods=['GET'])
+@api_bp.route('/repository//trigger/', methods=['GET'])
@api_login_required
@parse_repository_name
def list_build_triggers(namespace, repository):
@@ -1557,7 +1557,7 @@ def list_build_triggers(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//trigger/',
+@api_bp.route('/repository//trigger/',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@@ -1590,7 +1590,7 @@ def delete_build_trigger(namespace, repository, trigger_uuid):
abort(403) # Permission denied
-@api.route('/filedrop/', methods=['POST'])
+@api_bp.route('/filedrop/', methods=['POST'])
@api_login_required
@internal_api_call
def get_filedrop_url():
@@ -1617,7 +1617,7 @@ def wrap_role_view_org(role_json, user, org_members):
return role_json
-@api.route('/repository//image/', methods=['GET'])
+@api_bp.route('/repository//image/', methods=['GET'])
@parse_repository_name
def list_repository_images(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@@ -1642,7 +1642,7 @@ def list_repository_images(namespace, repository):
abort(403)
-@api.route('/repository//image/',
+@api_bp.route('/repository//image/',
methods=['GET'])
@parse_repository_name
def get_image(namespace, repository, image_id):
@@ -1656,7 +1656,7 @@ def get_image(namespace, repository, image_id):
abort(403)
-@api.route('/repository//image//changes',
+@api_bp.route('/repository//image//changes',
methods=['GET'])
@cache_control(max_age=60*60) # Cache for one hour
@parse_repository_name
@@ -1681,7 +1681,7 @@ def get_image_changes(namespace, repository, image_id):
abort(403)
-@api.route('/repository//tag/',
+@api_bp.route('/repository//tag/',
methods=['DELETE'])
@parse_repository_name
def delete_full_tag(namespace, repository, tag):
@@ -1700,7 +1700,7 @@ def delete_full_tag(namespace, repository, tag):
abort(403) # Permission denied
-@api.route('/repository//tag//images',
+@api_bp.route('/repository//tag//images',
methods=['GET'])
@parse_repository_name
def list_tag_images(namespace, repository, tag):
@@ -1724,7 +1724,7 @@ def list_tag_images(namespace, repository, tag):
abort(403) # Permission denied
-@api.route('/repository//permissions/team/',
+@api_bp.route('/repository//permissions/team/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1741,7 +1741,7 @@ def list_repo_team_permissions(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//permissions/user/',
+@api_bp.route('/repository//permissions/user/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1782,7 +1782,7 @@ def list_repo_user_permissions(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//permissions/user/',
+@api_bp.route('/repository//permissions/user/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1807,7 +1807,7 @@ def get_user_permissions(namespace, repository, username):
abort(403) # Permission denied
-@api.route('/repository//permissions/team/',
+@api_bp.route('/repository//permissions/team/',
methods=['GET'])
@api_login_required
@parse_repository_name
@@ -1822,7 +1822,7 @@ def get_team_permissions(namespace, repository, teamname):
abort(403) # Permission denied
-@api.route('/repository//permissions/user/',
+@api_bp.route('/repository//permissions/user/',
methods=['PUT', 'POST'])
@api_login_required
@parse_repository_name
@@ -1861,7 +1861,7 @@ def change_user_permissions(namespace, repository, username):
abort(403) # Permission denied
-@api.route('/repository//permissions/team/',
+@api_bp.route('/repository//permissions/team/',
methods=['PUT', 'POST'])
@api_login_required
@parse_repository_name
@@ -1889,7 +1889,7 @@ def change_team_permissions(namespace, repository, teamname):
abort(403) # Permission denied
-@api.route('/repository//permissions/user/',
+@api_bp.route('/repository//permissions/user/',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@@ -1910,7 +1910,7 @@ def delete_user_permissions(namespace, repository, username):
abort(403) # Permission denied
-@api.route('/repository//permissions/team/',
+@api_bp.route('/repository//permissions/team/',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@@ -1936,7 +1936,7 @@ def token_view(token_obj):
}
-@api.route('/repository//tokens/', methods=['GET'])
+@api_bp.route('/repository//tokens/', methods=['GET'])
@api_login_required
@parse_repository_name
def list_repo_tokens(namespace, repository):
@@ -1951,7 +1951,7 @@ def list_repo_tokens(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//tokens/', methods=['GET'])
+@api_bp.route('/repository//tokens/', methods=['GET'])
@api_login_required
@parse_repository_name
def get_tokens(namespace, repository, code):
@@ -1967,7 +1967,7 @@ def get_tokens(namespace, repository, code):
abort(403) # Permission denied
-@api.route('/repository//tokens/', methods=['POST'])
+@api_bp.route('/repository//tokens/', methods=['POST'])
@api_login_required
@parse_repository_name
def create_token(namespace, repository):
@@ -1989,7 +1989,7 @@ def create_token(namespace, repository):
abort(403) # Permission denied
-@api.route('/repository//tokens/', methods=['PUT'])
+@api_bp.route('/repository//tokens/', methods=['PUT'])
@api_login_required
@parse_repository_name
def change_token(namespace, repository, code):
@@ -2014,7 +2014,7 @@ def change_token(namespace, repository, code):
abort(403) # Permission denied
-@api.route('/repository//tokens/',
+@api_bp.route('/repository//tokens/',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@@ -2042,7 +2042,7 @@ def subscription_view(stripe_subscription, used_repos):
}
-@api.route('/user/card', methods=['GET'])
+@api_bp.route('/user/card', methods=['GET'])
@api_login_required
@internal_api_call
def get_user_card():
@@ -2050,7 +2050,7 @@ def get_user_card():
return get_card(user)
-@api.route('/organization//card', methods=['GET'])
+@api_bp.route('/organization//card', methods=['GET'])
@api_login_required
@internal_api_call
@org_api_call('get_user_card')
@@ -2063,7 +2063,7 @@ def get_org_card(orgname):
abort(403)
-@api.route('/user/card', methods=['POST'])
+@api_bp.route('/user/card', methods=['POST'])
@api_login_required
@internal_api_call
def set_user_card():
@@ -2074,7 +2074,7 @@ def set_user_card():
return response
-@api.route('/organization//card', methods=['POST'])
+@api_bp.route('/organization//card', methods=['POST'])
@api_login_required
@org_api_call('set_user_card')
def set_org_card(orgname):
@@ -2128,7 +2128,7 @@ def get_card(user):
return jsonify({'card': card_info})
-@api.route('/user/plan', methods=['PUT'])
+@api_bp.route('/user/plan', methods=['PUT'])
@api_login_required
@internal_api_call
def update_user_subscription():
@@ -2221,7 +2221,7 @@ def subscribe(user, plan, token, require_business_plan):
return resp
-@api.route('/user/invoices', methods=['GET'])
+@api_bp.route('/user/invoices', methods=['GET'])
@api_login_required
def list_user_invoices():
user = current_user.db_user()
@@ -2231,7 +2231,7 @@ def list_user_invoices():
return get_invoices(user.stripe_id)
-@api.route('/organization//invoices', methods=['GET'])
+@api_bp.route('/organization//invoices', methods=['GET'])
@api_login_required
@org_api_call('list_user_invoices')
def list_org_invoices(orgname):
@@ -2268,7 +2268,7 @@ def get_invoices(customer_id):
})
-@api.route('/organization//plan', methods=['PUT'])
+@api_bp.route('/organization//plan', methods=['PUT'])
@api_login_required
@internal_api_call
@org_api_call('update_user_subscription')
@@ -2284,7 +2284,7 @@ def update_org_subscription(orgname):
abort(403)
-@api.route('/user/plan', methods=['GET'])
+@api_bp.route('/user/plan', methods=['GET'])
@api_login_required
@internal_api_call
def get_user_subscription():
@@ -2303,7 +2303,7 @@ def get_user_subscription():
})
-@api.route('/organization//plan', methods=['GET'])
+@api_bp.route('/organization//plan', methods=['GET'])
@api_login_required
@internal_api_call
@org_api_call('get_user_subscription')
@@ -2333,7 +2333,7 @@ def robot_view(name, token):
}
-@api.route('/user/robots', methods=['GET'])
+@api_bp.route('/user/robots', methods=['GET'])
@api_login_required
def get_user_robots():
user = current_user.db_user()
@@ -2343,7 +2343,7 @@ def get_user_robots():
})
-@api.route('/organization//robots', methods=['GET'])
+@api_bp.route('/organization//robots', methods=['GET'])
@api_login_required
@org_api_call('get_user_robots')
def get_org_robots(orgname):
@@ -2357,7 +2357,7 @@ def get_org_robots(orgname):
abort(403)
-@api.route('/user/robots/', methods=['PUT'])
+@api_bp.route('/user/robots/', methods=['PUT'])
@api_login_required
def create_user_robot(robot_shortname):
parent = current_user.db_user()
@@ -2368,7 +2368,7 @@ def create_user_robot(robot_shortname):
return resp
-@api.route('/organization//robots/',
+@api_bp.route('/organization//robots/',
methods=['PUT'])
@api_login_required
@org_api_call('create_user_robot')
@@ -2385,7 +2385,7 @@ def create_org_robot(orgname, robot_shortname):
abort(403)
-@api.route('/user/robots/', methods=['DELETE'])
+@api_bp.route('/user/robots/', methods=['DELETE'])
@api_login_required
def delete_user_robot(robot_shortname):
parent = current_user.db_user()
@@ -2394,7 +2394,7 @@ def delete_user_robot(robot_shortname):
return make_response('Deleted', 204)
-@api.route('/organization//robots/',
+@api_bp.route('/organization//robots/',
methods=['DELETE'])
@api_login_required
@org_api_call('delete_user_robot')
@@ -2427,7 +2427,7 @@ def log_view(log):
-@api.route('/repository//logs', methods=['GET'])
+@api_bp.route('/repository//logs', methods=['GET'])
@api_login_required
@parse_repository_name
def list_repo_logs(namespace, repository):
@@ -2444,7 +2444,7 @@ def list_repo_logs(namespace, repository):
abort(403)
-@api.route('/organization//logs', methods=['GET'])
+@api_bp.route('/organization//logs', methods=['GET'])
@api_login_required
@org_api_call('list_user_logs')
def list_org_logs(orgname):
@@ -2460,7 +2460,7 @@ def list_org_logs(orgname):
abort(403)
-@api.route('/user/logs', methods=['GET'])
+@api_bp.route('/user/logs', methods=['GET'])
@api_login_required
def list_user_logs():
performer_name = request.args.get('performer', None)
diff --git a/endpoints/api/repository.py b/endpoints/api/repository.py
index 1f9dae710..8bda3a115 100644
--- a/endpoints/api/repository.py
+++ b/endpoints/api/repository.py
@@ -1,13 +1,14 @@
import logging
import json
-from flask.ext.restful import Resource, Api, reqparse, abort
+from flask.ext.restful import Resource, reqparse, abort
from flask.ext.login import current_user
from data import model
-from endpoints.api import (api, truthy_bool, format_date, nickname, log_action,
+from endpoints.api import (truthy_bool, format_date, nickname, log_action,
validate_json_request, require_repo_read,
- RepositoryParamResource)
+ RepositoryParamResource, resource, query_parameter,
+ parse_args)
from auth.permissions import (ReadRepositoryPermission,
ModifyRepositoryPermission,
AdministerRepositoryPermission)
@@ -15,15 +16,6 @@ from auth.permissions import (ReadRepositoryPermission,
logger = logging.getLogger(__name__)
-repo_api = Api(api)
-
-
-def resource(*urls, **kwargs):
- def wrapper(api_resource):
- repo_api.add_resource(api_resource, *urls, **kwargs)
- return api_resource
- return wrapper
-
@resource('/v1/repository')
class RepositoryList(Resource):
@@ -97,17 +89,21 @@ class RepositoryList(Resource):
abort(403)
@nickname('listRepos')
- def get(self):
- parser = reqparse.RequestParser()
- parser.add_argument('page', type=int, help='Page number must be an int.')
- parser.add_argument('limit', type=int, help='Limit must be an int.')
- parser.add_argument('namespace', type=str)
- parser.add_argument('public', type=truthy_bool, default=True)
- parser.add_argument('private', type=truthy_bool, default=True)
- parser.add_argument('sort', type=truthy_bool, default=False)
- parser.add_argument('count', type=truthy_bool, default=False)
- args = parser.parse_args()
-
+ @parse_args
+ @query_parameter('page', 'Offset page number. (int)', type=int)
+ @query_parameter('limit', 'Limit on the number of results (int)', type=int)
+ @query_parameter('namespace', ('Namespace to use when querying for org '
+ 'repositories.'), type=str)
+ @query_parameter('public', 'Whether to include public repositories.',
+ type=truthy_bool, default=True)
+ @query_parameter('private', 'Whether to inlcude private repositories.',
+ type=truthy_bool, default=True)
+ @query_parameter('sort', 'Whether to sort the results.', type=truthy_bool,
+ 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 repo_view(repo_obj):
return {
'namespace': repo_obj.namespace,