From 5c1d195a195aa72faf54fbd13d64328d45f83768 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 29 Jul 2015 18:25:44 -0400 Subject: [PATCH] Fix swagger errors Fixes #287 --- endpoints/api/build.py | 2 - endpoints/api/discovery.py | 63 +++++++++++++++++++------ endpoints/api/organization.py | 4 -- endpoints/api/permission.py | 2 - endpoints/api/prototype.py | 2 - endpoints/api/repository.py | 3 -- endpoints/api/repositorynotification.py | 1 - endpoints/api/repotoken.py | 2 - endpoints/api/suconfig.py | 3 -- endpoints/api/tag.py | 2 - endpoints/api/team.py | 1 - endpoints/api/trigger.py | 10 ++-- endpoints/api/user.py | 9 ---- static/js/services/api-service.js | 10 ++-- 14 files changed, 57 insertions(+), 57 deletions(-) diff --git a/endpoints/api/build.py b/endpoints/api/build.py index 9b9fd6de9..21002f4a3 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -146,7 +146,6 @@ class RepositoryBuildList(RepositoryParamResource): """ Resource related to creating and listing repository builds. """ schemas = { 'RepositoryBuildRequest': { - 'id': 'RepositoryBuildRequest', 'type': 'object', 'description': 'Description of a new repository build.', 'required': [ @@ -353,7 +352,6 @@ class FileDropResource(ApiResource): """ Custom verb for setting up a client side file transfer. """ schemas = { 'FileDropRequest': { - 'id': 'FileDropRequest', 'type': 'object', 'description': 'Description of the file that the user wishes to upload.', 'required': [ diff --git a/endpoints/api/discovery.py b/endpoints/api/discovery.py index e8d3a3e47..bcf45428b 100644 --- a/endpoints/api/discovery.py +++ b/endpoints/api/discovery.py @@ -3,6 +3,7 @@ import re import logging import sys +import copy from flask.ext.restful import reqparse @@ -35,7 +36,6 @@ def fully_qualified_name(method_view_class): def swagger_route_data(include_internal=False, compact=False): - 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 @@ -60,7 +60,6 @@ def swagger_route_data(include_internal=False, compact=False): return parameter_info - paths = {} models = {} tags = [] @@ -92,16 +91,17 @@ def swagger_route_data(include_internal=False, compact=False): # Build the Swagger data for the path. swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule) + full_name = fully_qualified_name(view_class) path_swagger = { - 'name': fully_qualified_name(view_class), - 'path': swagger_path, - 'tag': tag_name + 'x-name': full_name, + 'x-path': swagger_path, + 'x-tag': tag_name } if include_internal: related_user_res = method_metadata(view_class, 'related_user_resource') if related_user_res is not None: - path_swagger['quay_user_related'] = fully_qualified_name(related_user_res) + path_swagger['x-user-related'] = fully_qualified_name(related_user_res) paths[swagger_path] = path_swagger @@ -138,12 +138,12 @@ def swagger_route_data(include_internal=False, compact=False): # Mark the method as internal. internal = method_metadata(method, 'internal') if internal is not None: - operation_swagger['internal'] = True + operation_swagger['x-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 + operation_swagger['x-requires-fresh-login'] = True # Add the path parameters. if rule.arguments: @@ -169,14 +169,49 @@ def swagger_route_data(include_internal=False, compact=False): # 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}] + operation_swagger['security'] = [{'oauth2_implicit': [scope.scope]}] - # TODO: Add the responses block. + # 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 + if not compact: + if response_schema_name: + models[response_schema_name] = view_class.schemas[response_schema_name] + + responses = { + '400': { + 'description': 'Bad Request' + }, + + '401': { + 'description': 'Session required' + }, + + '403': { + 'description': 'Unauthorized access' + }, + + '404': { + 'description': 'Not found' + }, + } + + if method_name == 'DELETE': + responses['204'] = { + 'description': 'Deleted' + } + else: + responses['200'] = { + 'description': 'Successful invocation' + } + + if response_schema_name: + responses['200']['schema'] = { + '$ref': '#/definitions/%s' % response_schema_name + } + + operation_swagger['responses'] = responses + # Add the request block. request_schema_name = method_metadata(method, 'request_schema') @@ -192,7 +227,7 @@ def swagger_route_data(include_internal=False, compact=False): 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'])) + paths = OrderedDict(sorted(paths.items(), key=lambda p: p[1]['x-tag'])) if compact: return {'paths': paths} diff --git a/endpoints/api/organization.py b/endpoints/api/organization.py index 8bc21dbea..55da710a0 100644 --- a/endpoints/api/organization.py +++ b/endpoints/api/organization.py @@ -54,7 +54,6 @@ class OrganizationList(ApiResource): """ Resource for creating organizations. """ schemas = { 'NewOrg': { - 'id': 'NewOrg', 'type': 'object', 'description': 'Description of a new organization.', 'required': [ @@ -109,7 +108,6 @@ class Organization(ApiResource): """ Resource for managing organizations. """ schemas = { 'UpdateOrg': { - 'id': 'UpdateOrg', 'type': 'object', 'description': 'Description of updates for an existing organization', 'properties': { @@ -350,7 +348,6 @@ class OrganizationApplications(ApiResource): """ Resource for managing applications defined by an organization. """ schemas = { 'NewApp': { - 'id': 'NewApp', 'type': 'object', 'description': 'Description of a new organization application.', 'required': [ @@ -434,7 +431,6 @@ class OrganizationApplicationResource(ApiResource): """ Resource for managing an application defined by an organizations. """ schemas = { 'UpdateApp': { - 'id': 'UpdateApp', 'type': 'object', 'description': 'Description of an updated application.', 'required': [ diff --git a/endpoints/api/permission.py b/endpoints/api/permission.py index 6457ab72e..81150abb2 100644 --- a/endpoints/api/permission.py +++ b/endpoints/api/permission.py @@ -128,7 +128,6 @@ class RepositoryUserPermission(RepositoryParamResource): """ Resource for managing individual user permissions. """ schemas = { 'UserPermission': { - 'id': 'UserPermission', 'type': 'object', 'description': 'Description of a user permission.', 'required': [ @@ -223,7 +222,6 @@ class RepositoryTeamPermission(RepositoryParamResource): """ Resource for managing individual team permissions. """ schemas = { 'TeamPermission': { - 'id': 'TeamPermission', 'type': 'object', 'description': 'Description of a team permission.', 'required': [ diff --git a/endpoints/api/prototype.py b/endpoints/api/prototype.py index 018a4bd41..a9467b6a4 100644 --- a/endpoints/api/prototype.py +++ b/endpoints/api/prototype.py @@ -65,7 +65,6 @@ class PermissionPrototypeList(ApiResource): """ Resource for listing and creating permission prototypes. """ schemas = { 'NewPrototype': { - 'id': 'NewPrototype', 'type': 'object', 'description': 'Description of a new prototype', 'required': [ @@ -193,7 +192,6 @@ class PermissionPrototype(ApiResource): """ Resource for managingin individual permission prototypes. """ schemas = { 'PrototypeUpdate': { - 'id': 'PrototypeUpdate', 'type': 'object', 'description': 'Description of a the new prototype role', 'required': [ diff --git a/endpoints/api/repository.py b/endpoints/api/repository.py index 0f513b4c6..215931785 100644 --- a/endpoints/api/repository.py +++ b/endpoints/api/repository.py @@ -31,7 +31,6 @@ class RepositoryList(ApiResource): """Operations for creating and listing repositories.""" schemas = { 'NewRepo': { - 'id': 'NewRepo', 'type': 'object', 'description': 'Description of a new repository', 'required': [ @@ -204,7 +203,6 @@ class Repository(RepositoryParamResource): """Operations for managing a specific repository.""" schemas = { 'RepoUpdate': { - 'id': 'RepoUpdate', 'type': 'object', 'description': 'Fields which can be updated in a repository.', 'required': [ @@ -315,7 +313,6 @@ class RepositoryVisibility(RepositoryParamResource): """ Custom verb for changing the visibility of the repository. """ schemas = { 'ChangeVisibility': { - 'id': 'ChangeVisibility', 'type': 'object', 'description': 'Change the visibility for the repository.', 'required': [ diff --git a/endpoints/api/repositorynotification.py b/endpoints/api/repositorynotification.py index 876712c7b..54643ccc3 100644 --- a/endpoints/api/repositorynotification.py +++ b/endpoints/api/repositorynotification.py @@ -36,7 +36,6 @@ class RepositoryNotificationList(RepositoryParamResource): """ Resource for dealing with listing and creating notifications on a repository. """ schemas = { 'NotificationCreateRequest': { - 'id': 'NotificationCreateRequest', 'type': 'object', 'description': 'Information for creating a notification on a repository', 'required': [ diff --git a/endpoints/api/repotoken.py b/endpoints/api/repotoken.py index 8f07ff29e..46208c5ec 100644 --- a/endpoints/api/repotoken.py +++ b/endpoints/api/repotoken.py @@ -26,7 +26,6 @@ class RepositoryTokenList(RepositoryParamResource): """ Resource for creating and listing repository tokens. """ schemas = { 'NewToken': { - 'id': 'NewToken', 'type': 'object', 'description': 'Description of a new token.', 'required':[ @@ -74,7 +73,6 @@ class RepositoryToken(RepositoryParamResource): """ Resource for managing individual tokens. """ schemas = { 'TokenPermission': { - 'id': 'TokenPermission', 'type': 'object', 'description': 'Description of a token permission', 'required': [ diff --git a/endpoints/api/suconfig.py b/endpoints/api/suconfig.py index 576965bea..1b24da70c 100644 --- a/endpoints/api/suconfig.py +++ b/endpoints/api/suconfig.py @@ -162,7 +162,6 @@ class SuperUserConfig(ApiResource): """ Resource for fetching and updating the current configuration, if any. """ schemas = { 'UpdateConfig': { - 'id': 'UpdateConfig', 'type': 'object', 'description': 'Updates the YAML config file', 'required': [ @@ -273,7 +272,6 @@ class SuperUserCreateInitialSuperUser(ApiResource): """ Resource for creating the initial super user. """ schemas = { 'CreateSuperUser': { - 'id': 'CreateSuperUser', 'type': 'object', 'description': 'Information for creating the initial super user', 'required': [ @@ -346,7 +344,6 @@ class SuperUserConfigValidate(ApiResource): """ Resource for validating a block of configuration against an external service. """ schemas = { 'ValidateConfig': { - 'id': 'ValidateConfig', 'type': 'object', 'description': 'Validates configuration', 'required': [ diff --git a/endpoints/api/tag.py b/endpoints/api/tag.py index d3f8a04f9..a7b54fdf8 100644 --- a/endpoints/api/tag.py +++ b/endpoints/api/tag.py @@ -66,7 +66,6 @@ class RepositoryTag(RepositoryParamResource): """ Resource for managing repository tags. """ schemas = { 'MoveTag': { - 'id': 'MoveTag', 'type': 'object', 'description': 'Description of to which image a new or existing tag should point', 'required': [ @@ -162,7 +161,6 @@ class RevertTag(RepositoryParamResource): """ Resource for reverting a repository tag back to a previous image. """ schemas = { 'RevertTag': { - 'id': 'RevertTag', 'type': 'object', 'description': 'Reverts a tag to a specific image', 'required': [ diff --git a/endpoints/api/team.py b/endpoints/api/team.py index fa2091318..3eeff7b96 100644 --- a/endpoints/api/team.py +++ b/endpoints/api/team.py @@ -91,7 +91,6 @@ class OrganizationTeam(ApiResource): """ Resource for manging an organization's teams. """ schemas = { 'TeamDescription': { - 'id': 'TeamDescription', 'type': 'object', 'description': 'Description of a team', 'required': [ diff --git a/endpoints/api/trigger.py b/endpoints/api/trigger.py index d2d12731a..d44cb78a1 100644 --- a/endpoints/api/trigger.py +++ b/endpoints/api/trigger.py @@ -102,7 +102,6 @@ class BuildTriggerSubdirs(RepositoryParamResource): """ Custom verb for fetching the subdirs which are buildable for a trigger. """ schemas = { 'BuildTriggerSubdirRequest': { - 'id': 'BuildTriggerSubdirRequest', 'type': 'object', 'description': 'Arbitrary json.', }, @@ -151,7 +150,6 @@ class BuildTriggerActivate(RepositoryParamResource): """ schemas = { 'BuildTriggerActivateRequest': { - 'id': 'BuildTriggerActivateRequest', 'type': 'object', 'required': [ 'config' @@ -256,7 +254,6 @@ class BuildTriggerAnalyze(RepositoryParamResource): """ schemas = { 'BuildTriggerAnalyzeRequest': { - 'id': 'BuildTriggerAnalyzeRequest', 'type': 'object', 'required': [ 'config' @@ -393,20 +390,19 @@ class ActivateBuildTrigger(RepositoryParamResource): """ Custom verb to manually activate a build trigger. """ schemas = { 'RunParameters': { - 'id': 'RunParameters', 'type': 'object', 'description': 'Optional run parameters for activating the build trigger', - 'additional_properties': False, 'properties': { 'branch_name': { 'type': 'string', - 'description': '(SCM only) If specified, the name of the branch to model.build.' + 'description': '(SCM only) If specified, the name of the branch to build.' }, 'commit_sha': { 'type': 'string', 'description': '(Custom Only) If specified, the ref/SHA1 used to checkout a git repository.' } - } + }, + 'additionalProperties': False } } diff --git a/endpoints/api/user.py b/endpoints/api/user.py index cf0cf5794..de928597f 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -132,7 +132,6 @@ class User(ApiResource): """ Operations related to users. """ schemas = { 'NewUser': { - 'id': 'NewUser', 'type': 'object', 'description': 'Fields which must be specified for a new user.', 'required': [ @@ -160,7 +159,6 @@ class User(ApiResource): } }, 'UpdateUser': { - 'id': 'UpdateUser', 'type': 'object', 'description': 'Fields which can be updated in a user.', 'properties': { @@ -188,7 +186,6 @@ class User(ApiResource): }, }, 'UserView': { - 'id': 'UserView', 'type': 'object', 'description': 'Describes a user', 'required': ['verified', 'anonymous', 'avatar'], @@ -375,7 +372,6 @@ class ClientKey(ApiResource): for the Docker client. """ schemas = { 'GenerateClientKey': { - 'id': 'GenerateClientKey', 'type': 'object', 'required': [ 'password', @@ -443,7 +439,6 @@ class ConvertToOrganization(ApiResource): """ Operations for converting a user to an organization. """ schemas = { 'ConvertUser': { - 'id': 'ConvertUser', 'type': 'object', 'description': 'Information required to convert a user to an organization.', 'required': [ @@ -506,7 +501,6 @@ class Signin(ApiResource): """ Operations for signing in the user. """ schemas = { 'SigninUser': { - 'id': 'SigninUser', 'type': 'object', 'description': 'Information required to sign in a user.', 'required': [ @@ -619,7 +613,6 @@ class Recovery(ApiResource): """ Resource for requesting a password recovery email. """ schemas = { 'RequestRecovery': { - 'id': 'RequestRecovery', 'type': 'object', 'description': 'Information required to sign in a user.', 'required': [ @@ -676,7 +669,6 @@ class UserNotificationList(ApiResource): class UserNotification(ApiResource): schemas = { 'UpdateNotification': { - 'id': 'UpdateNotification', 'type': 'object', 'description': 'Information for updating a notification', 'properties': { @@ -771,7 +763,6 @@ class StarredRepositoryList(ApiResource): """ Operations for creating and listing starred repositories. """ schemas = { 'NewStarredRepository': { - 'id': 'NewStarredRepository', 'type': 'object', 'required': [ 'namespace', diff --git a/static/js/services/api-service.js b/static/js/services/api-service.js index 10c8b137f..14878a874 100644 --- a/static/js/services/api-service.js +++ b/static/js/services/api-service.js @@ -213,7 +213,7 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService' var buildMethodsForOperation = function(operation, method, path, resourceMap) { var operationName = operation['operationId']; - var urlPath = path['path']; + var urlPath = path['x-path']; // Add the operation itself. apiService[operationName] = function(opt_options, opt_parameters, opt_background, opt_forcessl) { @@ -228,7 +228,7 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService' // If the operation requires_fresh_login, then add a specialized error handler that // will defer the operation's result if sudo is requested. - if (operation['requires_fresh_login']) { + if (operation['x-requires-fresh-login']) { opObj = opObj.catch(freshLoginFailCheck(operationName, arguments)); } return opObj; @@ -244,8 +244,8 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService' // 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 (path['quay_user_related']) { - var userOperationName = getMatchingUserOperationName(operationName, method, resourceMap[path['quay_user_related']]); + if (path['x-user-related']) { + var userOperationName = getMatchingUserOperationName(operationName, method, resourceMap[path['x-user-related']]); var genericOperationName = getGenericOperationName(userOperationName); apiService[genericOperationName] = function(orgname, opt_options, opt_parameters, opt_background) { if (orgname) { @@ -287,7 +287,7 @@ angular.module('quay').factory('ApiService', ['Restangular', '$q', 'UtilService' // Build the map of resource names to their objects. forEachOperation(function(operation, method, path) { - resourceMap[path.name] = path; + resourceMap[path['x-name']] = path; }); // Construct the methods for each API endpoint.