diff --git a/endpoints/api/billing.py b/endpoints/api/billing.py
index 0342c9ea0..ef4028b9f 100644
--- a/endpoints/api/billing.py
+++ b/endpoints/api/billing.py
@@ -1,3 +1,5 @@
+""" Billing information, subscriptions, and plan information. """
+
import stripe
from flask import request
@@ -396,7 +398,7 @@ class UserInvoiceList(ApiResource):
@path_param('orgname', 'The name of the organization')
@related_user_resource(UserInvoiceList)
@show_if(features.BILLING)
-class OrgnaizationInvoiceList(ApiResource):
+class OrganizationInvoiceList(ApiResource):
""" Resource for listing an orgnaization's invoices. """
@require_scope(scopes.ORG_ADMIN)
@nickname('listOrgInvoices')
diff --git a/endpoints/api/build.py b/endpoints/api/build.py
index 385e9288c..05dd2114b 100644
--- a/endpoints/api/build.py
+++ b/endpoints/api/build.py
@@ -1,3 +1,5 @@
+""" Create, list, cancel and get status/logs of repository builds. """
+
import logging
import json
import time
@@ -165,7 +167,8 @@ class RepositoryBuildList(RepositoryParamResource):
},
'docker_tags': {
'type': 'array',
- 'description': 'The tags to which the built images will be pushed',
+ 'description': 'The tags to which the built images will be pushed. ' +
+ 'If none specified, "latest" is used.',
'items': {
'type': 'string'
},
diff --git a/endpoints/api/discovery.py b/endpoints/api/discovery.py
index 2481fa807..210410996 100644
--- a/endpoints/api/discovery.py
+++ b/endpoints/api/discovery.py
@@ -1,5 +1,8 @@
+""" API discovery information. """
+
import re
import logging
+import sys
from flask.ext.restful import reqparse
@@ -7,7 +10,7 @@ from endpoints.api import (ApiResource, resource, method_metadata, nickname, tru
parse_args, query_param)
from app import app
from auth import scopes
-
+from collections import OrderedDict
logger = logging.getLogger(__name__)
@@ -32,162 +35,197 @@ def fully_qualified_name(method_view_class):
def swagger_route_data(include_internal=False, compact=False):
- apis = []
+
+ def swagger_parameter(name, description, kind='path', param_type='string', required=True,
+ enum=None, schema=None):
+ # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parameterObject
+ parameter_info = {
+ 'name': name,
+ 'in': kind,
+ 'required': required
+ }
+
+ if not compact:
+ parameter_info['description'] = description or ''
+
+ if schema:
+ parameter_info['schema'] = {
+ '$ref': '#/definitions/%s' % schema
+ }
+ else:
+ parameter_info['type'] = param_type
+
+ if enum is not None and len(list(enum)) > 0:
+ parameter_info['enum'] = list(enum)
+
+ return parameter_info
+
+
+ paths = {}
models = {}
+ tags = []
+ tags_added = set()
+
for rule in app.url_map.iter_rules():
endpoint_method = app.view_functions[rule.endpoint]
- if 'view_class' in dir(endpoint_method):
- view_class = endpoint_method.view_class
+ # Verify that we have a view class for this API method.
+ if not 'view_class' in dir(endpoint_method):
+ continue
- param_data_map = {}
- if '__api_path_params' in dir(view_class):
- param_data_map = view_class.__api_path_params
+ view_class = endpoint_method.view_class
- operations = []
+ # Hide the class if it is internal.
+ internal = method_metadata(view_class, 'internal')
+ if not include_internal and internal:
+ continue
- method_names = list(rule.methods.difference(['HEAD', 'OPTIONS']))
- for method_name in method_names:
- method = getattr(view_class, method_name.lower(), None)
+ # Build the tag.
+ parts = fully_qualified_name(view_class).split('.')
+ tag_name = parts[-2]
+ if not tag_name in tags_added:
+ tags_added.add(tag_name)
+ tags.append({
+ 'name': tag_name,
+ 'description': (sys.modules[view_class.__module__].__doc__ or '').strip()
+ })
- parameters = []
-
- for param in rule.arguments:
- parameters.append({
- 'paramType': 'path',
- 'name': param,
- 'dataType': 'string',
- 'description': param_data_map.get(param, {'description': ''})['description'],
- 'required': True,
- })
-
- if method is None:
- logger.debug('Unable to find method for %s in class %s', method_name, view_class)
- else:
- req_schema_name = method_metadata(method, 'request_schema')
- if req_schema_name:
- parameters.append({
- 'paramType': 'body',
- 'name': 'body',
- 'description': 'Request body contents.',
- 'dataType': req_schema_name,
- 'required': True,
- })
-
- schema = view_class.schemas[req_schema_name]
- models[req_schema_name] = schema
-
- 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)
-
- new_operation = {
- 'method': method_name,
- 'nickname': method_metadata(method, 'nickname') or '(unnamed)'
- }
-
- if not compact:
- response_type = 'void'
- res_schema_name = method_metadata(method, 'response_schema')
- if res_schema_name:
- models[res_schema_name] = view_class.schemas[res_schema_name]
- response_type = res_schema_name
-
- new_operation.update({
- 'type': response_type,
- 'summary': method.__doc__.strip() if method.__doc__ else '',
- 'parameters': parameters,
- })
-
-
- scope = method_metadata(method, 'oauth2_scope')
- if scope and not compact:
- new_operation['authorizations'] = {
- 'oauth2': [
- {
- 'scope': scope.scope
- }
- ],
- }
-
- internal = method_metadata(method, 'internal')
- if internal is not None:
- new_operation['internal'] = True
-
- if include_internal:
- requires_fresh_login = method_metadata(method, 'requires_fresh_login')
- if requires_fresh_login is not None:
- new_operation['requires_fresh_login'] = True
-
- if not internal or (internal and include_internal):
- # Swagger requires valid nicknames on all operations.
- if new_operation.get('nickname'):
- operations.append(new_operation)
- else:
- logger.debug('Operation missing nickname: %s' % method)
-
- swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
- new_resource = {
- 'path': swagger_path,
- 'description': view_class.__doc__.strip() if view_class.__doc__ else "",
- 'operations': operations,
- 'name': fully_qualified_name(view_class),
- }
+ # Build the Swagger data for the path.
+ swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
+ path_swagger = {
+ 'name': fully_qualified_name(view_class),
+ 'path': swagger_path,
+ 'tag': tag_name
+ }
+ if include_internal:
related_user_res = method_metadata(view_class, 'related_user_resource')
if related_user_res is not None:
- new_resource['quayUserRelated'] = fully_qualified_name(related_user_res)
+ path_swagger['quay_user_related'] = fully_qualified_name(related_user_res)
- internal = method_metadata(view_class, 'internal')
+ paths[swagger_path] = path_swagger
+
+ # Add any global path parameters.
+ param_data_map = view_class.__api_path_params if '__api_path_params' in dir(view_class) else {}
+ if param_data_map:
+ path_parameters_swagger = []
+ for path_parameter in param_data_map:
+ description = param_data_map[path_parameter].get('description')
+ path_parameters_swagger.append(swagger_parameter(path_parameter, description))
+
+ path_swagger['parameters'] = path_parameters_swagger
+
+ # Add the individual HTTP operations.
+ method_names = list(rule.methods.difference(['HEAD', 'OPTIONS']))
+ for method_name in method_names:
+ # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#operation-object
+ method = getattr(view_class, method_name.lower(), None)
+ if method is None:
+ logger.debug('Unable to find method for %s in class %s', method_name, view_class)
+ continue
+
+ operation_swagger = {
+ 'operationId': method_metadata(method, 'nickname') or 'unnamed',
+ 'parameters': [],
+ }
+
+ if not compact:
+ operation_swagger.update({
+ 'description': method.__doc__.strip() if method.__doc__ else '',
+ 'tags': [tag_name]
+ })
+
+ # Mark the method as internal.
+ internal = method_metadata(method, 'internal')
if internal is not None:
- new_resource['internal'] = True
+ operation_swagger['internal'] = True
+ if include_internal:
+ requires_fresh_login = method_metadata(method, 'requires_fresh_login')
+ if requires_fresh_login is not None:
+ operation_swagger['requires_fresh_login'] = True
+
+ # Add the path parameters.
+ if rule.arguments:
+ for path_parameter in rule.arguments:
+ description = param_data_map.get(path_parameter, {}).get('description')
+ operation_swagger['parameters'].append(swagger_parameter(path_parameter, description))
+
+ # Add the query parameters.
+ if '__api_query_params' in dir(method):
+ for query_parameter_info in method.__api_query_params:
+ name = query_parameter_info['name']
+ description = query_parameter_info['help']
+ param_type = TYPE_CONVERTER[query_parameter_info['type']]
+ required = query_parameter_info['required']
+
+ operation_swagger['parameters'].append(
+ swagger_parameter(name, description, kind='query',
+ param_type=param_type,
+ required=required,
+ enum=query_parameter_info['choices']))
+
+ # Add the OAuth security block.
+ # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#securityRequirementObject
+ scope = method_metadata(method, 'oauth2_scope')
+ if scope and not compact:
+ operation_swagger['security'] = [{'oauth2_implicit': scope.scope}]
+
+ # TODO: Add the responses block.
+ # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#responsesObject
+ response_schema_name = method_metadata(method, 'response_schema')
+ if response_schema_name and not compact:
+ models[response_schema_name] = view_class.schemas[response_schema_name]
+ response_type = response_schema_name
+
+ # Add the request block.
+ request_schema_name = method_metadata(method, 'request_schema')
+ if request_schema_name and not compact:
+ models[request_schema_name] = view_class.schemas[request_schema_name]
+
+ operation_swagger['parameters'].append(
+ swagger_parameter('body', 'Request body contents.', kind='body',
+ schema=request_schema_name))
+
+ # Add the operation to the parent path.
if not internal or (internal and include_internal):
- apis.append(new_resource)
+ path_swagger[method_name.lower()] = operation_swagger
+
+ tags.sort(key=lambda t: t['name'])
+ paths = OrderedDict(sorted(paths.items(), key=lambda p: p[1]['tag']))
- # If compact form was requested, simply return the APIs.
if compact:
- return {'apis': apis}
+ return {'paths': paths}
swagger_data = {
- 'apiVersion': 'v1',
- 'swaggerVersion': '1.2',
- 'basePath': '%s://%s' % (PREFERRED_URL_SCHEME, SERVER_HOSTNAME),
- 'resourcePath': '/',
+ 'swagger': '2.0',
+ 'host': SERVER_HOSTNAME,
+ 'basePath': '/',
+ 'schemes': [
+ PREFERRED_URL_SCHEME
+ ],
'info': {
- 'title': 'Quay.io API',
+ 'version': 'v1',
+ 'title': 'Quay.io Frontend',
'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',
+ 'termsOfService': 'https://quay.io/tos',
+ 'contact': {
+ 'email': 'support@quay.io'
+ }
},
- 'authorizations': {
- 'oauth2': {
- 'scopes': [scope._asdict() for scope in scopes.ALL_SCOPES.values()],
- 'grantTypes': {
- "implicit": {
- "tokenName": "access_token",
- "loginEndpoint": {
- "url": "%s://%s/oauth/authorize" % (PREFERRED_URL_SCHEME, SERVER_HOSTNAME),
- },
- },
- },
+ 'securityDefinitions': {
+ 'oauth2_implicit': {
+ "type": "oauth2",
+ "flow": "implicit",
+ "authorizationUrl": "%s://%s/oauth/authorize" % (PREFERRED_URL_SCHEME, SERVER_HOSTNAME),
+ 'scopes': {scope.scope:scope.description for scope in scopes.ALL_SCOPES.values()},
},
},
- 'apis': apis,
- 'models': models,
+ 'paths': paths,
+ 'definitions': models,
+ 'tags': tags
}
return swagger_data
diff --git a/endpoints/api/image.py b/endpoints/api/image.py
index 939a87d98..a85dce084 100644
--- a/endpoints/api/image.py
+++ b/endpoints/api/image.py
@@ -1,3 +1,5 @@
+""" List and lookup repository images, and download image diffs. """
+
import json
from collections import defaultdict
diff --git a/endpoints/api/logs.py b/endpoints/api/logs.py
index d83cff202..55c6110ad 100644
--- a/endpoints/api/logs.py
+++ b/endpoints/api/logs.py
@@ -1,3 +1,5 @@
+""" Access usage logs for organizations or repositories. """
+
import json
from datetime import datetime, timedelta
diff --git a/endpoints/api/organization.py b/endpoints/api/organization.py
index 3cb98fb84..849e85c71 100644
--- a/endpoints/api/organization.py
+++ b/endpoints/api/organization.py
@@ -1,3 +1,5 @@
+""" Manage organizations, members and OAuth applications. """
+
import logging
from flask import request
@@ -333,7 +335,7 @@ def app_view(application):
@resource('/v1/organization//applications')
@path_param('orgname', 'The name of the organization')
class OrganizationApplications(ApiResource):
- """ Resource for managing applications defined by an organizations. """
+ """ Resource for managing applications defined by an organization. """
schemas = {
'NewApp': {
'id': 'NewApp',
diff --git a/endpoints/api/permission.py b/endpoints/api/permission.py
index 4c0b62074..6e160d688 100644
--- a/endpoints/api/permission.py
+++ b/endpoints/api/permission.py
@@ -1,3 +1,5 @@
+""" Manage repository permissions. """
+
import logging
from flask import request
diff --git a/endpoints/api/prototype.py b/endpoints/api/prototype.py
index de0c97483..d34b3db25 100644
--- a/endpoints/api/prototype.py
+++ b/endpoints/api/prototype.py
@@ -1,3 +1,5 @@
+""" Manage default permissions added to repositories. """
+
from flask import request
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
diff --git a/endpoints/api/repoemail.py b/endpoints/api/repoemail.py
index bb448721a..76c5938f3 100644
--- a/endpoints/api/repoemail.py
+++ b/endpoints/api/repoemail.py
@@ -1,3 +1,5 @@
+""" Authorize repository to send e-mail notifications. """
+
import logging
from flask import request, abort
diff --git a/endpoints/api/repository.py b/endpoints/api/repository.py
index bbc1b49de..802a0e36f 100644
--- a/endpoints/api/repository.py
+++ b/endpoints/api/repository.py
@@ -1,3 +1,5 @@
+""" List, create and manage repositories. """
+
import logging
import json
import datetime
diff --git a/endpoints/api/repositorynotification.py b/endpoints/api/repositorynotification.py
index 92ea4315c..c5742fd62 100644
--- a/endpoints/api/repositorynotification.py
+++ b/endpoints/api/repositorynotification.py
@@ -1,3 +1,5 @@
+""" List, create and manage repository events/notifications. """
+
import json
from flask import request, abort
diff --git a/endpoints/api/repotoken.py b/endpoints/api/repotoken.py
index a6b28275b..2b64efcd9 100644
--- a/endpoints/api/repotoken.py
+++ b/endpoints/api/repotoken.py
@@ -1,3 +1,5 @@
+""" Manage repository access tokens (DEPRECATED). """
+
import logging
from flask import request
diff --git a/endpoints/api/robot.py b/endpoints/api/robot.py
index fb6cf74ac..b1967f59d 100644
--- a/endpoints/api/robot.py
+++ b/endpoints/api/robot.py
@@ -1,3 +1,5 @@
+""" Manage user and organization robot accounts. """
+
from endpoints.api import (resource, nickname, ApiResource, log_action, related_user_resource,
Unauthorized, require_user_admin, internal_only, require_scope,
path_param, parse_args, truthy_bool, query_param)
@@ -84,7 +86,6 @@ class UserRobotList(ApiResource):
@resource('/v1/user/robots/')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
-@internal_only
class UserRobot(ApiResource):
""" Resource for managing a user's robots. """
@require_user_admin
@@ -180,7 +181,6 @@ class OrgRobot(ApiResource):
@resource('/v1/user/robots//permissions')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
-@internal_only
class UserRobotPermissions(ApiResource):
""" Resource for listing the permissions a user's robot has in the system. """
@require_user_admin
@@ -221,7 +221,6 @@ class OrgRobotPermissions(ApiResource):
@resource('/v1/user/robots//regenerate')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
-@internal_only
class RegenerateUserRobot(ApiResource):
""" Resource for regenerate an organization's robot's token. """
@require_user_admin
diff --git a/endpoints/api/search.py b/endpoints/api/search.py
index 375be7b77..d8928e84a 100644
--- a/endpoints/api/search.py
+++ b/endpoints/api/search.py
@@ -1,3 +1,5 @@
+""" Conduct searches against all registry context. """
+
from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, nickname, resource,
require_scope, path_param)
from data import model
diff --git a/endpoints/api/subscribe.py b/endpoints/api/subscribe.py
index fc93c330f..501b8e881 100644
--- a/endpoints/api/subscribe.py
+++ b/endpoints/api/subscribe.py
@@ -1,3 +1,5 @@
+""" Subscribe to plans. """
+
import logging
import stripe
diff --git a/endpoints/api/suconfig.py b/endpoints/api/suconfig.py
index b2fa941cd..de9d74029 100644
--- a/endpoints/api/suconfig.py
+++ b/endpoints/api/suconfig.py
@@ -1,3 +1,5 @@
+""" Superuser Config API. """
+
import logging
import os
import json
diff --git a/endpoints/api/superuser.py b/endpoints/api/superuser.py
index 46857343a..4f70f0761 100644
--- a/endpoints/api/superuser.py
+++ b/endpoints/api/superuser.py
@@ -1,3 +1,5 @@
+""" Superuser API. """
+
import string
import logging
import json
diff --git a/endpoints/api/tag.py b/endpoints/api/tag.py
index 9ca880e3f..6e7c33a0a 100644
--- a/endpoints/api/tag.py
+++ b/endpoints/api/tag.py
@@ -1,3 +1,5 @@
+""" Manage the tags of a repository. """
+
from flask import request, abort
from endpoints.api import (resource, nickname, require_repo_read, require_repo_write,
diff --git a/endpoints/api/team.py b/endpoints/api/team.py
index ce42f5e94..1dc95b3d1 100644
--- a/endpoints/api/team.py
+++ b/endpoints/api/team.py
@@ -1,3 +1,5 @@
+""" Create, list and manage an organization's teams. """
+
from flask import request
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
diff --git a/endpoints/api/trigger.py b/endpoints/api/trigger.py
index 8706ccf31..727f97731 100644
--- a/endpoints/api/trigger.py
+++ b/endpoints/api/trigger.py
@@ -1,3 +1,5 @@
+""" Create, list and manage build triggers. """
+
import json
import logging
@@ -397,7 +399,7 @@ class ActivateBuildTrigger(RepositoryParamResource):
'properties': {
'branch_name': {
'type': 'string',
- 'description': '(GitHub Only) If specified, the name of the GitHub branch to build.'
+ 'description': '(SCM only) If specified, the name of the branch to build.'
},
'commit_sha': {
'type': 'string',
diff --git a/endpoints/api/user.py b/endpoints/api/user.py
index 585b75eda..8e9d2d2e3 100644
--- a/endpoints/api/user.py
+++ b/endpoints/api/user.py
@@ -1,3 +1,5 @@
+""" Manage the current user. """
+
import logging
import json
@@ -183,11 +185,17 @@ class User(ApiResource):
},
'organizations': {
'type': 'array',
- 'description': 'Information about the organizations in which the user is a member'
+ 'description': 'Information about the organizations in which the user is a member',
+ 'items': {
+ 'type': 'object'
+ }
},
'logins': {
'type': 'array',
- 'description': 'The list of external login providers against which the user has authenticated'
+ 'description': 'The list of external login providers against which the user has authenticated',
+ 'items': {
+ 'type': 'object'
+ }
},
'can_create_repo': {
'type': 'boolean',
@@ -807,10 +815,10 @@ class StarredRepositoryList(ApiResource):
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class StarredRepository(RepositoryParamResource):
""" Operations for managing a specific starred repository. """
-
@nickname('deleteStar')
@require_user_admin
def delete(self, namespace, repository):
+ """ Removes a star from a repository. """
user = get_authenticated_user()
repo = model.get_repository(namespace, repository)
diff --git a/static/js/services/api-service.js b/static/js/services/api-service.js
index 5952463fd..10c8b137f 100644
--- a/static/js/services/api-service.js
+++ b/static/js/services/api-service.js
@@ -101,27 +101,14 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService'
var getMatchingUserOperationName = function(orgOperationName, method, userRelatedResource) {
if (userRelatedResource) {
- var operations = userRelatedResource['operations'];
- for (var i = 0; i < operations.length; ++i) {
- var operation = operations[i];
- if (operation['method'].toLowerCase() == method) {
- return operation['nickname'];
- }
+ if (userRelatedResource[method.toLowerCase()]) {
+ return userRelatedResource[method.toLowerCase()]['operationId'];
}
}
throw new Error('Could not find user operation matching org operation: ' + orgOperationName);
};
- var buildMethodsForEndpointResource = function(endpointResource, resourceMap) {
- var name = endpointResource['name'];
- var operations = endpointResource['operations'];
- for (var i = 0; i < operations.length; ++i) {
- var operation = operations[i];
- buildMethodsForOperation(operation, endpointResource, resourceMap);
- }
- };
-
var freshLoginInProgress = [];
var reject = function(msg) {
for (var i = 0; i < freshLoginInProgress.length; ++i) {
@@ -224,14 +211,13 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService'
};
};
- var buildMethodsForOperation = function(operation, resource, resourceMap) {
- var method = operation['method'].toLowerCase();
- var operationName = operation['nickname'];
- var path = resource['path'];
+ var buildMethodsForOperation = function(operation, method, path, resourceMap) {
+ var operationName = operation['operationId'];
+ var urlPath = path['path'];
// Add the operation itself.
apiService[operationName] = function(opt_options, opt_parameters, opt_background, opt_forcessl) {
- var one = Restangular.one(buildUrl(path, opt_parameters, opt_forcessl));
+ var one = Restangular.one(buildUrl(urlPath, opt_parameters, opt_forcessl));
if (opt_background) {
one.withHttpConfig({
'ignoreLoadingBar': true
@@ -251,15 +237,15 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService'
// If the method for the operation is a GET, add an operationAsResource method.
if (method == 'get') {
apiService[operationName + 'AsResource'] = function(opt_parameters, opt_background) {
- return getResource(buildUrl(path, opt_parameters), opt_background);
+ return getResource(buildUrl(urlPath, opt_parameters), opt_background);
};
}
- // If the resource has a user-related resource, then make a generic operation for this operation
+ // If the operation has a user-related operation, then make a generic operation for this operation
// that can call both the user and the organization versions of the operation, depending on the
// parameters given.
- if (resource['quayUserRelated']) {
- var userOperationName = getMatchingUserOperationName(operationName, method, resourceMap[resource['quayUserRelated']]);
+ if (path['quay_user_related']) {
+ var userOperationName = getMatchingUserOperationName(operationName, method, resourceMap[path['quay_user_related']]);
var genericOperationName = getGenericOperationName(userOperationName);
apiService[genericOperationName] = function(orgname, opt_options, opt_parameters, opt_background) {
if (orgname) {
@@ -280,19 +266,34 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService'
return apiService;
}
+ var allowedMethods = ['get', 'post', 'put', 'delete'];
var resourceMap = {};
+ var forEachOperation = function(callback) {
+ for (var path in window.__endpoints) {
+ if (!window.__endpoints.hasOwnProperty(path)) {
+ continue;
+ }
+
+ for (var method in window.__endpoints[path]) {
+ if (!window.__endpoints[path].hasOwnProperty(method)) {
+ continue;
+ }
+
+ if (allowedMethods.indexOf(method.toLowerCase()) < 0) { continue; }
+ callback(window.__endpoints[path][method], method, window.__endpoints[path]);
+ }
+ }
+ };
// Build the map of resource names to their objects.
- for (var i = 0; i < window.__endpoints.length; ++i) {
- var endpointResource = window.__endpoints[i];
- resourceMap[endpointResource['name']] = endpointResource;
- }
+ forEachOperation(function(operation, method, path) {
+ resourceMap[path.name] = path;
+ });
// Construct the methods for each API endpoint.
- for (var i = 0; i < window.__endpoints.length; ++i) {
- var endpointResource = window.__endpoints[i];
- buildMethodsForEndpointResource(endpointResource, resourceMap);
- }
+ forEachOperation(function(operation, method, path) {
+ buildMethodsForOperation(operation, method, path, resourceMap);
+ });
apiService.getErrorMessage = function(resp, defaultMessage) {
var message = defaultMessage;
diff --git a/templates/base.html b/templates/base.html
index 91dee7842..77f28c2a0 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -41,7 +41,7 @@
{% endblock %}