{"action":"created","comment":{"url":"https://api.github.com/repos/pulp/pulp/pulls/comments/42912807","id":42912807,"diff_hunk":"@@ -0,0 +1,281 @@\n+import logging\n+import sys\n+\n+import celery\n+from mongoengine import ValidationError\n+\n+from pulp.common import error_codes, tags\n+from pulp.plugins.config import PluginCallConfiguration\n+from pulp.plugins.loader import api as plugin_api\n+from pulp.server import exceptions\n+from pulp.server.async.tasks import Task\n+from pulp.server.db import model\n+from pulp.server.managers import factory as manager_factory\n+\n+\n+_logger = logging.getLogger(__name__)\n+\n+\n+def build_resource_tag(repo_id, importer_type_id):\n+    \"\"\"\n+    Returns an identfifier for the repo and importer.\n+\n+    :param repo_id: unique ID for a repository\n+    :type  repo_id: basestring\n+    :param importer_type_id: unique ID for the importer\n+    :type  importer_type_id: basestring\n+\n+    :return: a globally unique identifier for the repo and importer that\n+             can be used in cross-type comparisons.\n+    :rtype:  basestring\n+    \"\"\"\n+    RESOURCE_TEMPLATE = 'pulp:importer:%s:%s'\n+    return RESOURCE_TEMPLATE % (repo_id, importer_type_id)\n+\n+\n+def clean_config_dict(config):\n+    \"\"\"\n+    Remove keys from a dict that have a value None.\n+\n+    :param config: configuration for plugin\n+    :type  config: dict\n+\n+    :return: config without the keys whose values were None\n+    :rtype:  dict:\n+    \"\"\"\n+\n+    if config is not None:\n+        return dict([(k, v) for k, v in config.items() if v is not None])\n+    else:\n+        return None\n+\n+\n+@celery.task(base=Task, name='pulp.server.managers.repo.importer.set_importer')\n+def set_importer(repo_id, importer_type_id, repo_plugin_config):\n+    \"\"\"\n+    Configures an importer to be used for the given repository.\n+\n+    :param repo_id: identifies the repo\n+    :type  repo_id: str\n+    :param importer_type_id: type of importer, must correspond to a plugin loaded at server startup\n+    :type  importer_type_id: str\n+    :param repo_plugin_config: configuration values for the importer; may be None\n+    :type  repo_plugin_config: dict or None\n+\n+    :return: created importer object\n+    :rtype:  pulp.server.model.Importer\n+\n+    :raises PulpExecutionException: if something goes wrong in the plugin\n+    :raises exceptions.InvalidValue: if the values passed to create the importer are invalid\n+    \"\"\"\n+    validate_importer_config(repo_id, importer_type_id, repo_plugin_config)\n+    repo_obj = model.Repository.objects.get_repo_or_missing_resource(repo_id)\n+\n+    importer_instance, plugin_config = plugin_api.get_importer_by_id(importer_type_id)\n+    clean_config = clean_config_dict(repo_plugin_config)\n+\n+    # Let the importer plugin verify the configuration\n+    call_config = PluginCallConfiguration(plugin_config, clean_config)\n+    transfer_repo = repo_obj.to_transfer_repo()\n+\n+    try:\n+        remove_importer(repo_id)\n+    except exceptions.MissingResource:\n+        pass  # it didn't exist, so no harm done\n+\n+    # Let the importer plugin initialize the importer\n+    try:\n+        importer_instance.importer_added(transfer_repo, call_config)\n+    except Exception:\n+        _logger.exception(\n+            'Error initializing importer [%s] for repo [%s]' % (importer_type_id, repo_id))\n+        raise exceptions.PulpExecutionException(), None, sys.exc_info()[2]\n+\n+    importer = model.Importer(repo_id, importer_type_id, clean_config)\n+    try:\n+        importer.save()\n+    except ValidationError, e:\n+        raise exceptions.InvalidValue(e.to_dict().keys())\n+\n+    return importer\n+\n+\n+def queue_set_importer(repo_id, importer_type_id, config):\n+    \"\"\"\n+    Dispatch a task to set the importer on a repository.\n+\n+    :param repo_id: identifies the repo\n+    :type  repo_id: str\n+    :param importer_type_id: type of importer, must correspond to a plugin loaded at server startup\n+    :type  importer_type_id: str\n+    :param config: configuration values for the importer\n+    :type  config: dict or None\n+\n+    :return: asynchronous result\n+    :rtype:  pulp.server.async.tasks.TaskResult\n+    \"\"\"\n+    task_tags = [tags.resource_tag(tags.RESOURCE_REPOSITORY_TYPE, repo_id),\n+                 tags.action_tag('add_importer')]\n+    async_result = set_importer.apply_async_with_reservation(\n+        tags.RESOURCE_REPOSITORY_TYPE, repo_id, [repo_id, importer_type_id],\n+        {'repo_plugin_config': config}, tags=task_tags)\n+    return async_result\n+\n+\n+def validate_importer_config(repo_id, importer_type_id, importer_config):\n+    \"\"\"\n+    This validates that the repository and importer type exist as these are both required to\n+    validate the configuration.\n+\n+    :param repo_id: identifies the repo\n+    :type  repo_id: str\n+    :param importer_type_id: type of importer, must correspond to a plugin loaded at server startup\n+    :type  importer_type_id: str\n+    :param importer_config: configuration values for the importer; may be None\n+    :type  importer_config: dict\n+\n+    :raises exceptions.PulpCodedValidationException: if config is invalid.\n+    \"\"\"\n+    repo_obj = model.Repository.objects.get_repo_or_missing_resource(repo_id)\n+\n+    if not plugin_api.is_valid_importer(importer_type_id):\n+        raise exceptions.PulpCodedValidationException(error_code=error_codes.PLP1008,\n+                                                      importer_type_id=importer_type_id)\n+\n+    importer_instance, plugin_config = plugin_api.get_importer_by_id(importer_type_id)\n+    clean_config = clean_config_dict(importer_config)\n+    call_config = PluginCallConfiguration(plugin_config, clean_config)\n+    transfer_repo = repo_obj.to_transfer_repo()\n+    result = importer_instance.validate_config(transfer_repo, call_config)\n+\n+    # For backward compatibility with plugins that don't yet return the tuple\n+    if isinstance(result, bool):\n+        valid_config = result\n+        message = None\n+    else:\n+        valid_config, message = result\n+\n+    if not valid_config:\n+        raise exceptions.PulpCodedValidationException(validation_errors=message)\n+\n+\n+@celery.task(base=Task, name='pulp.server.managers.repo.importer.remove_importer')\n+def remove_importer(repo_id):\n+    \"\"\"\n+    Removes an importer from a repository.\n+\n+    :param repo_id: identifies the repo\n+    :type  repo_id: str\n+    \"\"\"\n+    repo_obj = model.Repository.objects.get_repo_or_missing_resource(repo_id)\n+    repo_importer = model.Importer.objects.get_or_404(repo_id=repo_id)\n+\n+    # remove schedules\n+    sync_manager = manager_factory.repo_sync_schedule_manager()\n+    sync_manager.delete_by_importer_id(repo_id, repo_importer.importer_type_id)\n+\n+    # Call the importer's cleanup method\n+    importer_instance, plugin_config = plugin_api.get_importer_by_id(repo_importer.importer_type_id)\n+\n+    call_config = PluginCallConfiguration(plugin_config, repo_importer.config)\n+    transfer_repo = repo_obj.to_transfer_repo()\n+    importer_instance.importer_removed(transfer_repo, call_config)\n+    repo_importer.delete()\n+\n+\n+def queue_remove_importer(repo_id, importer_type_id):\n+    \"\"\"\n+    Dispatch a task to remove the importer from a repository.\n+\n+    :param repo_id: identifies the repo\n+    :type  repo_id: str\n+    :param importer_type_id: type of importer\n+    :type  importer_type_id: str\n+\n+    :return: asynchronous result\n+    :rtype:  pulp.server.async.tasks.TaskResult\n+    \"\"\"\n+    get_valid_importer(repo_id, importer_type_id)\n+    task_tags = [tags.resource_tag(tags.RESOURCE_REPOSITORY_TYPE, repo_id),\n+                 tags.resource_tag(tags.RESOURCE_REPOSITORY_IMPORTER_TYPE, importer_type_id),\n+                 tags.action_tag('delete_importer')]\n+    async_result = remove_importer.apply_async_with_reservation(\n+        tags.RESOURCE_REPOSITORY_TYPE, repo_id, [repo_id], tags=task_tags)\n+    return async_result\n+\n+\n+def get_valid_importer(repo_id, importer_type_id):\n+    \"\"\"\n+    Retrieves the importer for the specified repo and ensures that the provided importer type id\n+    matches the importer set on the repo.\n+\n+    :param repo_id: id of the repo\n+    :type  repo_id: str\n+    :param importer_type_id: type of the importer\n+    :type  importer_type_id: str\n+\n+    :return: key-value pairs describing the importer in use\n+    :rtype:  dict\n+\n+    :raises pulp.server.exceptions.MissingResource: if repo/importer combination cannot be found\n+    \"\"\"\n+    model.Repository.objects.get_repo_or_missing_resource(repo_id)\n+    try:\n+        importer = model.Importer.objects.get_or_404(repo_id=repo_id)\n+        if importer.importer_type_id != importer_type_id:\n+            raise exceptions.MissingResource(importer_id=importer_type_id)","path":"server/pulp/server/controllers/importer.py","position":226,"original_position":226,"commit_id":"f07ad585a6a860f67fa800037a6155b82a8438d2","original_commit_id":"f07ad585a6a860f67fa800037a6155b82a8438d2","user":{"login":"mhrivnak","id":53091,"avatar_url":"https://avatars.githubusercontent.com/u/53091?v=3","gravatar_id":"","url":"https://api.github.com/users/mhrivnak","html_url":"https://github.com/mhrivnak","followers_url":"https://api.github.com/users/mhrivnak/followers","following_url":"https://api.github.com/users/mhrivnak/following{/other_user}","gists_url":"https://api.github.com/users/mhrivnak/gists{/gist_id}","starred_url":"https://api.github.com/users/mhrivnak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhrivnak/subscriptions","organizations_url":"https://api.github.com/users/mhrivnak/orgs","repos_url":"https://api.github.com/users/mhrivnak/repos","events_url":"https://api.github.com/users/mhrivnak/events{/privacy}","received_events_url":"https://api.github.com/users/mhrivnak/received_events","type":"User","site_admin":false},"body":"This will get caught on the next line, thrown away, and replaced by a new but identical exception. One solution is to move these two lines below the ``except`` block. Then you at least have two distinct code paths that end up raising the same exception.","created_at":"2015-10-23T20:49:47Z","updated_at":"2015-10-23T20:49:47Z","html_url":"https://github.com/pulp/pulp/pull/2114#discussion_r42912807","pull_request_url":"https://api.github.com/repos/pulp/pulp/pulls/2114","_links":{"self":{"href":"https://api.github.com/repos/pulp/pulp/pulls/comments/42912807"},"html":{"href":"https://github.com/pulp/pulp/pull/2114#discussion_r42912807"},"pull_request":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2114"}}},"pull_request":{"url":"https://api.github.com/repos/pulp/pulp/pulls/2114","id":47704818,"html_url":"https://github.com/pulp/pulp/pull/2114","diff_url":"https://github.com/pulp/pulp/pull/2114.diff","patch_url":"https://github.com/pulp/pulp/pull/2114.patch","issue_url":"https://api.github.com/repos/pulp/pulp/issues/2114","number":2114,"state":"open","locked":false,"title":"Convert repo importers collection to mongoengine.","user":{"login":"asmacdo","id":1028657,"avatar_url":"https://avatars.githubusercontent.com/u/1028657?v=3","gravatar_id":"","url":"https://api.github.com/users/asmacdo","html_url":"https://github.com/asmacdo","followers_url":"https://api.github.com/users/asmacdo/followers","following_url":"https://api.github.com/users/asmacdo/following{/other_user}","gists_url":"https://api.github.com/users/asmacdo/gists{/gist_id}","starred_url":"https://api.github.com/users/asmacdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asmacdo/subscriptions","organizations_url":"https://api.github.com/users/asmacdo/orgs","repos_url":"https://api.github.com/users/asmacdo/repos","events_url":"https://api.github.com/users/asmacdo/events{/privacy}","received_events_url":"https://api.github.com/users/asmacdo/received_events","type":"User","site_admin":false},"body":"closes: #782","created_at":"2015-10-14T19:38:51Z","updated_at":"2015-10-23T20:49:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a2da7b11dca995ee1ce13ff30a30b63e522539f1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/pulp/pulp/pulls/2114/commits","review_comments_url":"https://api.github.com/repos/pulp/pulp/pulls/2114/comments","review_comment_url":"https://api.github.com/repos/pulp/pulp/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pulp/pulp/issues/2114/comments","statuses_url":"https://api.github.com/repos/pulp/pulp/statuses/f07ad585a6a860f67fa800037a6155b82a8438d2","head":{"label":"asmacdo:importers-mongoengine","ref":"importers-mongoengine","sha":"f07ad585a6a860f67fa800037a6155b82a8438d2","user":{"login":"asmacdo","id":1028657,"avatar_url":"https://avatars.githubusercontent.com/u/1028657?v=3","gravatar_id":"","url":"https://api.github.com/users/asmacdo","html_url":"https://github.com/asmacdo","followers_url":"https://api.github.com/users/asmacdo/followers","following_url":"https://api.github.com/users/asmacdo/following{/other_user}","gists_url":"https://api.github.com/users/asmacdo/gists{/gist_id}","starred_url":"https://api.github.com/users/asmacdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asmacdo/subscriptions","organizations_url":"https://api.github.com/users/asmacdo/orgs","repos_url":"https://api.github.com/users/asmacdo/repos","events_url":"https://api.github.com/users/asmacdo/events{/privacy}","received_events_url":"https://api.github.com/users/asmacdo/received_events","type":"User","site_admin":false},"repo":{"id":22577579,"name":"pulp","full_name":"asmacdo/pulp","owner":{"login":"asmacdo","id":1028657,"avatar_url":"https://avatars.githubusercontent.com/u/1028657?v=3","gravatar_id":"","url":"https://api.github.com/users/asmacdo","html_url":"https://github.com/asmacdo","followers_url":"https://api.github.com/users/asmacdo/followers","following_url":"https://api.github.com/users/asmacdo/following{/other_user}","gists_url":"https://api.github.com/users/asmacdo/gists{/gist_id}","starred_url":"https://api.github.com/users/asmacdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asmacdo/subscriptions","organizations_url":"https://api.github.com/users/asmacdo/orgs","repos_url":"https://api.github.com/users/asmacdo/repos","events_url":"https://api.github.com/users/asmacdo/events{/privacy}","received_events_url":"https://api.github.com/users/asmacdo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/asmacdo/pulp","description":"Repository for Pulp platform code, including the server and base admin and consumer clients","fork":true,"url":"https://api.github.com/repos/asmacdo/pulp","forks_url":"https://api.github.com/repos/asmacdo/pulp/forks","keys_url":"https://api.github.com/repos/asmacdo/pulp/keys{/key_id}","collaborators_url":"https://api.github.com/repos/asmacdo/pulp/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/asmacdo/pulp/teams","hooks_url":"https://api.github.com/repos/asmacdo/pulp/hooks","issue_events_url":"https://api.github.com/repos/asmacdo/pulp/issues/events{/number}","events_url":"https://api.github.com/repos/asmacdo/pulp/events","assignees_url":"https://api.github.com/repos/asmacdo/pulp/assignees{/user}","branches_url":"https://api.github.com/repos/asmacdo/pulp/branches{/branch}","tags_url":"https://api.github.com/repos/asmacdo/pulp/tags","blobs_url":"https://api.github.com/repos/asmacdo/pulp/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/asmacdo/pulp/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/asmacdo/pulp/git/refs{/sha}","trees_url":"https://api.github.com/repos/asmacdo/pulp/git/trees{/sha}","statuses_url":"https://api.github.com/repos/asmacdo/pulp/statuses/{sha}","languages_url":"https://api.github.com/repos/asmacdo/pulp/languages","stargazers_url":"https://api.github.com/repos/asmacdo/pulp/stargazers","contributors_url":"https://api.github.com/repos/asmacdo/pulp/contributors","subscribers_url":"https://api.github.com/repos/asmacdo/pulp/subscribers","subscription_url":"https://api.github.com/repos/asmacdo/pulp/subscription","commits_url":"https://api.github.com/repos/asmacdo/pulp/commits{/sha}","git_commits_url":"https://api.github.com/repos/asmacdo/pulp/git/commits{/sha}","comments_url":"https://api.github.com/repos/asmacdo/pulp/comments{/number}","issue_comment_url":"https://api.github.com/repos/asmacdo/pulp/issues/comments{/number}","contents_url":"https://api.github.com/repos/asmacdo/pulp/contents/{+path}","compare_url":"https://api.github.com/repos/asmacdo/pulp/compare/{base}...{head}","merges_url":"https://api.github.com/repos/asmacdo/pulp/merges","archive_url":"https://api.github.com/repos/asmacdo/pulp/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/asmacdo/pulp/downloads","issues_url":"https://api.github.com/repos/asmacdo/pulp/issues{/number}","pulls_url":"https://api.github.com/repos/asmacdo/pulp/pulls{/number}","milestones_url":"https://api.github.com/repos/asmacdo/pulp/milestones{/number}","notifications_url":"https://api.github.com/repos/asmacdo/pulp/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/asmacdo/pulp/labels{/name}","releases_url":"https://api.github.com/repos/asmacdo/pulp/releases{/id}","created_at":"2014-08-03T15:27:48Z","updated_at":"2015-03-09T15:25:38Z","pushed_at":"2015-10-23T13:26:49Z","git_url":"git://github.com/asmacdo/pulp.git","ssh_url":"git@github.com:asmacdo/pulp.git","clone_url":"https://github.com/asmacdo/pulp.git","svn_url":"https://github.com/asmacdo/pulp","homepage":"","size":88311,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pulp:master","ref":"master","sha":"544f42b89d099e5a26c92240d2d4e64c93d8e9aa","user":{"login":"pulp","id":2103606,"avatar_url":"https://avatars.githubusercontent.com/u/2103606?v=3","gravatar_id":"","url":"https://api.github.com/users/pulp","html_url":"https://github.com/pulp","followers_url":"https://api.github.com/users/pulp/followers","following_url":"https://api.github.com/users/pulp/following{/other_user}","gists_url":"https://api.github.com/users/pulp/gists{/gist_id}","starred_url":"https://api.github.com/users/pulp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pulp/subscriptions","organizations_url":"https://api.github.com/users/pulp/orgs","repos_url":"https://api.github.com/users/pulp/repos","events_url":"https://api.github.com/users/pulp/events{/privacy}","received_events_url":"https://api.github.com/users/pulp/received_events","type":"Organization","site_admin":false},"repo":{"id":5314652,"name":"pulp","full_name":"pulp/pulp","owner":{"login":"pulp","id":2103606,"avatar_url":"https://avatars.githubusercontent.com/u/2103606?v=3","gravatar_id":"","url":"https://api.github.com/users/pulp","html_url":"https://github.com/pulp","followers_url":"https://api.github.com/users/pulp/followers","following_url":"https://api.github.com/users/pulp/following{/other_user}","gists_url":"https://api.github.com/users/pulp/gists{/gist_id}","starred_url":"https://api.github.com/users/pulp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pulp/subscriptions","organizations_url":"https://api.github.com/users/pulp/orgs","repos_url":"https://api.github.com/users/pulp/repos","events_url":"https://api.github.com/users/pulp/events{/privacy}","received_events_url":"https://api.github.com/users/pulp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pulp/pulp","description":"Repository for Pulp platform code, including the server and base admin and consumer clients","fork":false,"url":"https://api.github.com/repos/pulp/pulp","forks_url":"https://api.github.com/repos/pulp/pulp/forks","keys_url":"https://api.github.com/repos/pulp/pulp/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pulp/pulp/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pulp/pulp/teams","hooks_url":"https://api.github.com/repos/pulp/pulp/hooks","issue_events_url":"https://api.github.com/repos/pulp/pulp/issues/events{/number}","events_url":"https://api.github.com/repos/pulp/pulp/events","assignees_url":"https://api.github.com/repos/pulp/pulp/assignees{/user}","branches_url":"https://api.github.com/repos/pulp/pulp/branches{/branch}","tags_url":"https://api.github.com/repos/pulp/pulp/tags","blobs_url":"https://api.github.com/repos/pulp/pulp/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pulp/pulp/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pulp/pulp/git/refs{/sha}","trees_url":"https://api.github.com/repos/pulp/pulp/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pulp/pulp/statuses/{sha}","languages_url":"https://api.github.com/repos/pulp/pulp/languages","stargazers_url":"https://api.github.com/repos/pulp/pulp/stargazers","contributors_url":"https://api.github.com/repos/pulp/pulp/contributors","subscribers_url":"https://api.github.com/repos/pulp/pulp/subscribers","subscription_url":"https://api.github.com/repos/pulp/pulp/subscription","commits_url":"https://api.github.com/repos/pulp/pulp/commits{/sha}","git_commits_url":"https://api.github.com/repos/pulp/pulp/git/commits{/sha}","comments_url":"https://api.github.com/repos/pulp/pulp/comments{/number}","issue_comment_url":"https://api.github.com/repos/pulp/pulp/issues/comments{/number}","contents_url":"https://api.github.com/repos/pulp/pulp/contents/{+path}","compare_url":"https://api.github.com/repos/pulp/pulp/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pulp/pulp/merges","archive_url":"https://api.github.com/repos/pulp/pulp/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pulp/pulp/downloads","issues_url":"https://api.github.com/repos/pulp/pulp/issues{/number}","pulls_url":"https://api.github.com/repos/pulp/pulp/pulls{/number}","milestones_url":"https://api.github.com/repos/pulp/pulp/milestones{/number}","notifications_url":"https://api.github.com/repos/pulp/pulp/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pulp/pulp/labels{/name}","releases_url":"https://api.github.com/repos/pulp/pulp/releases{/id}","created_at":"2012-08-06T13:49:20Z","updated_at":"2015-10-20T20:35:57Z","pushed_at":"2015-10-23T18:39:09Z","git_url":"git://github.com/pulp/pulp.git","ssh_url":"git@github.com:pulp/pulp.git","clone_url":"https://github.com/pulp/pulp.git","svn_url":"https://github.com/pulp/pulp","homepage":"","size":210339,"stargazers_count":228,"watchers_count":228,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":93,"mirror_url":null,"open_issues_count":15,"forks":93,"open_issues":15,"watchers":228,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2114"},"html":{"href":"https://github.com/pulp/pulp/pull/2114"},"issue":{"href":"https://api.github.com/repos/pulp/pulp/issues/2114"},"comments":{"href":"https://api.github.com/repos/pulp/pulp/issues/2114/comments"},"review_comments":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2114/comments"},"review_comment":{"href":"https://api.github.com/repos/pulp/pulp/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2114/commits"},"statuses":{"href":"https://api.github.com/repos/pulp/pulp/statuses/f07ad585a6a860f67fa800037a6155b82a8438d2"}}}}