{"action":"created","comment":{"url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/comments/35357272","id":35357272,"diff_hunk":"@@ -931,3 +937,325 @@ def update(self, fields=None):\n \n         \"\"\"\n         return self.read(attrs=self.update_json(fields))\n+\n+\n+class EntitySearchMixin(object):\n+    \"\"\"This mixin provides the ability to search for or get a list of entities.\n+\n+    The methods provided by this class work together. The call tree looks like\n+    this::\n+\n+        search\n+        ├── search_json\n+        │   └── search_raw\n+        │       └── search_payload\n+        ├── search_normalize\n+        └── search_filter\n+\n+    In short, here is what the methods do:\n+\n+    :meth:`search_payload`\n+        Assemble a search query that can be encoded and sent to the server.\n+    :meth:`search_raw`\n+        Make an HTTP GET request to the server, including the payload.\n+    :meth:`search_json`\n+        Check the server's response for errors and decode the response.\n+    :meth:`search_normalize`\n+        Normalize search results so they can be used to create new entities.\n+    :meth:`search`\n+        Create one or more :class:`nailgun.entity_mixins.Entity` objects\n+        representing the found entities and populate their fields.\n+    :meth:`search_filter`\n+        Read all ``entities`` and locally filter them.\n+\n+    See the individual methods for more detailed information.\n+\n+    \"\"\"\n+\n+    def search_payload(self, fields=None, query=None):\n+        \"\"\"Create a search query.\n+\n+        Do the following:\n+\n+        1. Generate a search query. If ``fields`` is specified, all named\n+           fields are included in the query. Otherwise, all populated fields\n+           are included.\n+        2. Merge ``query`` in to the generated search query.\n+        3. Return the result.\n+\n+        The rules for generating a search query are simple, and can be\n+        illustrated by example::\n+\n+            >>> SomeEntity(\n+            ...     id=1,\n+            ...     one_to_one=2,\n+            ...     one_to_many=[3, 4],\n+            ... ).generate_payload()\n+            {'id': 1, 'one_id': 2, 'many_ids': [3, 4]},\n+\n+        .. WARNING:: This method currently generates an extremely naive search\n+            query that will be wrong in many cases.\n+\n+        :param fields: See :meth:`search`.\n+        :param query: See :meth:`search`.\n+        :returns: A dict that can be encoded as JSON and used in a search.\n+\n+        \"\"\"\n+        if fields is None:\n+            fields = set(self.get_values().keys())\n+        if query is None:\n+            query = {}\n+\n+        payload = {}\n+        fields_dict = self.get_fields()\n+        for field in fields:\n+            value = getattr(self, field)\n+            if isinstance(fields_dict[field], OneToOneField):\n+                payload[field + '_id'] = value.id\n+            elif isinstance(fields_dict[field], OneToManyField):\n+                payload[field + '_ids'] = [entity.id for entity in value]\n+            else:\n+                payload[field] = value\n+        payload.update(query)\n+        return payload\n+\n+    def search_raw(self, fields=None, query=None):\n+        \"\"\"Search for entities.\n+\n+        Make an HTTP GET call to ``self.path('base')``. Return the response.\n+\n+        .. WARNING:: Subclasses should not fiddle with the ``fields`` or\n+            ``query`` arguments. See :meth:`search_normalize` for details.\n+\n+        :param fields: See :meth:`search`.\n+        :param query: See :meth:`search`.\n+        :return: A ``requests.response`` object.\n+\n+        \"\"\"\n+        return client.get(\n+            self.path('base'),\n+            data=self.search_payload(fields, query),\n+            **self._server_config.get_client_kwargs()\n+        )\n+\n+    def search_json(self, fields=None, query=None):\n+        \"\"\"Search for entities.\n+\n+        Call :meth:`search_raw`. Check the response status code, decode JSON\n+        and return the decoded JSON as a dict.\n+\n+        .. WARNING:: Subclasses should not fiddle with the ``fields`` or\n+            ``query`` arguments. See :meth:`search_normalize` for details.\n+\n+        :param fields: See :meth:`search`.\n+        :param query: See :meth:`search`.\n+        :return: A dict. The server's response, with all JSON decoded.\n+        :raises: ``requests.exceptions.HTTPError`` if the response has an HTTP\n+            4XX or 5XX status code.\n+        :raises: ``ValueError`` If the response JSON can not be decoded.\n+\n+        \"\"\"\n+        response = self.search_raw(fields, query)\n+        response.raise_for_status()\n+        return response.json()\n+\n+    def search_normalize(self, results):\n+        \"\"\"Normalize search results so they can be used to create new entities.\n+\n+        See :meth:`search` for an example of how to use this method. Here's a\n+        simplified example::\n+\n+            results = self.search_json()\n+            results = self.search_normalize(results)\n+            entity = SomeEntity(some_cfg, **results[0])\n+\n+        At this time, it is possible to parse all search results without\n+        knowing what search query was used. However, it is possible that\n+        certain responses can only be parsed if the search query is known. If\n+        that is the case, this method will be given a new ``payload`` argument,\n+        where ``payload`` is what was sent to the server. This will only work\n+        if ``search_raw`` and ``search`` are able to generate identical\n+        payloads. Pending extensive use of :class:`EntitySearchMixin`:\n+\n+        * :meth:`search` may alter ``fields`` and ``query`` at will.\n+        * :meth:`search_payload` may alter ``fields`` and ``query`` in an\n+          idempotent manner.\n+        * No other method should atler ``fields`` or ``query``.\n+\n+        :param results: A list of dicts, where each dict is a set of attributes\n+            for one entity. The contents of these dicts are as is returned from\n+            the server.\n+        :returns: A list of dicts, where each dict is a set of attributes for\n+            one entity. The contents of these dicts have been normalized and\n+            can be used to instantiate entities.\n+\n+        \"\"\"\n+        fields = self.get_fields()\n+        normalized = []\n+        for result in results:\n+            # For each field that we know about, copy the corresponding field\n+            # from the server's search result. If any extra attributes are\n+            # copied over, Entity.__init__ will raise a NoSuchFieldError.\n+            # Examples of problematic results from server:\n+            #\n+            # * organization_id (denormalized OneToOne. see above)\n+            # * organizations, organization_ids (denormalized OneToMany. above)\n+            # * updated_at, created_at (these may be handled in the future)\n+            # * sp_subnet (Host.sp_subnet is an undocumented field)\n+            #\n+            attrs = {}\n+            for field_name, field in fields.items():\n+                if isinstance(field, OneToOneField):\n+                    try:\n+                        attrs[field_name] = _get_entity_id(field_name, result)\n+                    except MissingValueError:\n+                        pass\n+                elif isinstance(field, OneToManyField):\n+                    try:\n+                        attrs[field_name] = _get_entity_ids(field_name, result)\n+                    except MissingValueError:\n+                        pass\n+                else:\n+                    try:\n+                        attrs[field_name] = result[field_name]\n+                    except KeyError:\n+                        pass\n+            normalized.append(attrs)\n+        return normalized\n+\n+    def search(self, fields=None, query=None, filters=None):\n+        \"\"\"Search for entities.\n+\n+        At its simplest, this method searches for all entities of a given kind.\n+        For example, to ask for all :class:`nailgun.entities.Organization`\n+        entities::\n+\n+            Organization().search()\n+\n+        Values on an entity are used to generate a search query, and the\n+        ``fields`` argument can be used to specify which fields should be used\n+        when generating a search query::\n+\n+            org = Organization(name='foo', description='bar')\n+            results = org.search()  # Search by name and description.\n+            results = org.search(fields={'name', 'description'})  # Same.\n+            results = org.search(fields={'name'})  # Search by name.\n+            results = org.search(fields={'description'})  # Search by desc\n+            results = org.search(fields=set())  # Search for all organizations.\n+            results = org.search(fields={'title'})  # Error!\n+\n+        In some cases, the simple search queries that can be generated by\n+        NailGun are not sufficient. In this case, you can pass in a raw search\n+        query instead. For example, to search for all organizations with a name\n+        of 'foo'::\n+\n+            Organization().search(query={'search': u'name=\"foo\"'})\n+\n+        The example above is rather pointless: it is easier and more concise to\n+        use a generated query. But — and this is a **very** important \"but\" —\n+        the manual search query is melded in to the generated query. This can\n+        be used to great effect::\n+\n+            Organization(name='foo').search(query={'per_page': 50})\n+\n+        In some cases, the server's search facilities may be insufficient, or\n+        it may be inordinately difficult to craft a search query. In this case,\n+        you can filter search results locally. For example, to ask the server\n+        for a list of all organizations and then locally search through the\n+        results for the organization named \"foo\"::\n+\n+            Organization().search(filters={'name': 'foo'})","path":"nailgun/entity_mixins.py","position":264,"original_position":264,"commit_id":"a45b32557da000e801ce6905f06ee2326a174992","original_commit_id":"a45b32557da000e801ce6905f06ee2326a174992","user":{"login":"Ichimonji10","id":1012989,"avatar_url":"https://avatars.githubusercontent.com/u/1012989?v=3","gravatar_id":"","url":"https://api.github.com/users/Ichimonji10","html_url":"https://github.com/Ichimonji10","followers_url":"https://api.github.com/users/Ichimonji10/followers","following_url":"https://api.github.com/users/Ichimonji10/following{/other_user}","gists_url":"https://api.github.com/users/Ichimonji10/gists{/gist_id}","starred_url":"https://api.github.com/users/Ichimonji10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Ichimonji10/subscriptions","organizations_url":"https://api.github.com/users/Ichimonji10/orgs","repos_url":"https://api.github.com/users/Ichimonji10/repos","events_url":"https://api.github.com/users/Ichimonji10/events{/privacy}","received_events_url":"https://api.github.com/users/Ichimonji10/received_events","type":"User","site_admin":false},"body":"The `filters` attribute is the only portion of this PR that I find to be really questionable:\r\n\r\n* Someone who knows what they're doing can easily write a list comprehension that does everything `search_filter()` does and more (filtering on foreign key fields is not supported) quite trivially.\r\n* I think it'd be more beneficial for us to focus our efforts on auto-generating good quality searches. That's a much tougher problem. Users shouldn't have to write queries all the time, which is something they have to do right now.\r\n* I expect it'd be more useful to have something like an `expect` argument, where you can tell nailgun how many results you expect to be returned by any given query.","created_at":"2015-07-23T18:49:40Z","updated_at":"2015-07-23T18:49:40Z","html_url":"https://github.com/SatelliteQE/nailgun/pull/157#discussion_r35357272","pull_request_url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157","_links":{"self":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/comments/35357272"},"html":{"href":"https://github.com/SatelliteQE/nailgun/pull/157#discussion_r35357272"},"pull_request":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157"}}},"pull_request":{"url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157","id":40694250,"html_url":"https://github.com/SatelliteQE/nailgun/pull/157","diff_url":"https://github.com/SatelliteQE/nailgun/pull/157.diff","patch_url":"https://github.com/SatelliteQE/nailgun/pull/157.patch","issue_url":"https://api.github.com/repos/SatelliteQE/nailgun/issues/157","number":157,"state":"open","locked":false,"title":"[DO NOT MERGE] EntitySearchMixin","user":{"login":"Ichimonji10","id":1012989,"avatar_url":"https://avatars.githubusercontent.com/u/1012989?v=3","gravatar_id":"","url":"https://api.github.com/users/Ichimonji10","html_url":"https://github.com/Ichimonji10","followers_url":"https://api.github.com/users/Ichimonji10/followers","following_url":"https://api.github.com/users/Ichimonji10/following{/other_user}","gists_url":"https://api.github.com/users/Ichimonji10/gists{/gist_id}","starred_url":"https://api.github.com/users/Ichimonji10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Ichimonji10/subscriptions","organizations_url":"https://api.github.com/users/Ichimonji10/orgs","repos_url":"https://api.github.com/users/Ichimonji10/repos","events_url":"https://api.github.com/users/Ichimonji10/events{/privacy}","received_events_url":"https://api.github.com/users/Ichimonji10/received_events","type":"User","site_admin":false},"body":"The first commit implements and tests class `EntitySearchMixin`. The second commit integrates the mixin with several entity classes. See the individual commit messages for more details.\r\n\r\nThe current NailGun version is 0.18.2. I'd like to release version 0.19.0 today, then include this change in 0.20.0. I am marking this PR as \"DO NOT MERGE\" for that reason, but am posting the PR now to collect feedback and to keep track of it.\r\n\r\nFor proof that this PR functions correctly, see <del>FIXME: Fetch information from the future</del> <ins>SatelliteQE/robottelo#2610</ins>.","created_at":"2015-07-23T17:00:38Z","updated_at":"2015-07-23T18:49:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f9a5b11bd9645a0c0df46b26178a7e20242c0abf","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157/commits","review_comments_url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157/comments","review_comment_url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SatelliteQE/nailgun/issues/157/comments","statuses_url":"https://api.github.com/repos/SatelliteQE/nailgun/statuses/a45b32557da000e801ce6905f06ee2326a174992","head":{"label":"Ichimonji10:search2","ref":"search2","sha":"a45b32557da000e801ce6905f06ee2326a174992","user":{"login":"Ichimonji10","id":1012989,"avatar_url":"https://avatars.githubusercontent.com/u/1012989?v=3","gravatar_id":"","url":"https://api.github.com/users/Ichimonji10","html_url":"https://github.com/Ichimonji10","followers_url":"https://api.github.com/users/Ichimonji10/followers","following_url":"https://api.github.com/users/Ichimonji10/following{/other_user}","gists_url":"https://api.github.com/users/Ichimonji10/gists{/gist_id}","starred_url":"https://api.github.com/users/Ichimonji10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Ichimonji10/subscriptions","organizations_url":"https://api.github.com/users/Ichimonji10/orgs","repos_url":"https://api.github.com/users/Ichimonji10/repos","events_url":"https://api.github.com/users/Ichimonji10/events{/privacy}","received_events_url":"https://api.github.com/users/Ichimonji10/received_events","type":"User","site_admin":false},"repo":{"id":34913792,"name":"nailgun","full_name":"Ichimonji10/nailgun","owner":{"login":"Ichimonji10","id":1012989,"avatar_url":"https://avatars.githubusercontent.com/u/1012989?v=3","gravatar_id":"","url":"https://api.github.com/users/Ichimonji10","html_url":"https://github.com/Ichimonji10","followers_url":"https://api.github.com/users/Ichimonji10/followers","following_url":"https://api.github.com/users/Ichimonji10/following{/other_user}","gists_url":"https://api.github.com/users/Ichimonji10/gists{/gist_id}","starred_url":"https://api.github.com/users/Ichimonji10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Ichimonji10/subscriptions","organizations_url":"https://api.github.com/users/Ichimonji10/orgs","repos_url":"https://api.github.com/users/Ichimonji10/repos","events_url":"https://api.github.com/users/Ichimonji10/events{/privacy}","received_events_url":"https://api.github.com/users/Ichimonji10/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Ichimonji10/nailgun","description":"Why use a hammer when you can use a nailgun.","fork":true,"url":"https://api.github.com/repos/Ichimonji10/nailgun","forks_url":"https://api.github.com/repos/Ichimonji10/nailgun/forks","keys_url":"https://api.github.com/repos/Ichimonji10/nailgun/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Ichimonji10/nailgun/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Ichimonji10/nailgun/teams","hooks_url":"https://api.github.com/repos/Ichimonji10/nailgun/hooks","issue_events_url":"https://api.github.com/repos/Ichimonji10/nailgun/issues/events{/number}","events_url":"https://api.github.com/repos/Ichimonji10/nailgun/events","assignees_url":"https://api.github.com/repos/Ichimonji10/nailgun/assignees{/user}","branches_url":"https://api.github.com/repos/Ichimonji10/nailgun/branches{/branch}","tags_url":"https://api.github.com/repos/Ichimonji10/nailgun/tags","blobs_url":"https://api.github.com/repos/Ichimonji10/nailgun/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Ichimonji10/nailgun/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Ichimonji10/nailgun/git/refs{/sha}","trees_url":"https://api.github.com/repos/Ichimonji10/nailgun/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Ichimonji10/nailgun/statuses/{sha}","languages_url":"https://api.github.com/repos/Ichimonji10/nailgun/languages","stargazers_url":"https://api.github.com/repos/Ichimonji10/nailgun/stargazers","contributors_url":"https://api.github.com/repos/Ichimonji10/nailgun/contributors","subscribers_url":"https://api.github.com/repos/Ichimonji10/nailgun/subscribers","subscription_url":"https://api.github.com/repos/Ichimonji10/nailgun/subscription","commits_url":"https://api.github.com/repos/Ichimonji10/nailgun/commits{/sha}","git_commits_url":"https://api.github.com/repos/Ichimonji10/nailgun/git/commits{/sha}","comments_url":"https://api.github.com/repos/Ichimonji10/nailgun/comments{/number}","issue_comment_url":"https://api.github.com/repos/Ichimonji10/nailgun/issues/comments{/number}","contents_url":"https://api.github.com/repos/Ichimonji10/nailgun/contents/{+path}","compare_url":"https://api.github.com/repos/Ichimonji10/nailgun/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Ichimonji10/nailgun/merges","archive_url":"https://api.github.com/repos/Ichimonji10/nailgun/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Ichimonji10/nailgun/downloads","issues_url":"https://api.github.com/repos/Ichimonji10/nailgun/issues{/number}","pulls_url":"https://api.github.com/repos/Ichimonji10/nailgun/pulls{/number}","milestones_url":"https://api.github.com/repos/Ichimonji10/nailgun/milestones{/number}","notifications_url":"https://api.github.com/repos/Ichimonji10/nailgun/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Ichimonji10/nailgun/labels{/name}","releases_url":"https://api.github.com/repos/Ichimonji10/nailgun/releases{/id}","created_at":"2015-05-01T17:12:25Z","updated_at":"2015-05-07T15:42:47Z","pushed_at":"2015-07-23T18:03:45Z","git_url":"git://github.com/Ichimonji10/nailgun.git","ssh_url":"git@github.com:Ichimonji10/nailgun.git","clone_url":"https://github.com/Ichimonji10/nailgun.git","svn_url":"https://github.com/Ichimonji10/nailgun","homepage":null,"size":963,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"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":"SatelliteQE:master","ref":"master","sha":"0c53f42f7ced37c97df4eb0c386744abfafc1ec9","user":{"login":"SatelliteQE","id":8754276,"avatar_url":"https://avatars.githubusercontent.com/u/8754276?v=3","gravatar_id":"","url":"https://api.github.com/users/SatelliteQE","html_url":"https://github.com/SatelliteQE","followers_url":"https://api.github.com/users/SatelliteQE/followers","following_url":"https://api.github.com/users/SatelliteQE/following{/other_user}","gists_url":"https://api.github.com/users/SatelliteQE/gists{/gist_id}","starred_url":"https://api.github.com/users/SatelliteQE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SatelliteQE/subscriptions","organizations_url":"https://api.github.com/users/SatelliteQE/orgs","repos_url":"https://api.github.com/users/SatelliteQE/repos","events_url":"https://api.github.com/users/SatelliteQE/events{/privacy}","received_events_url":"https://api.github.com/users/SatelliteQE/received_events","type":"Organization","site_admin":false},"repo":{"id":27888295,"name":"nailgun","full_name":"SatelliteQE/nailgun","owner":{"login":"SatelliteQE","id":8754276,"avatar_url":"https://avatars.githubusercontent.com/u/8754276?v=3","gravatar_id":"","url":"https://api.github.com/users/SatelliteQE","html_url":"https://github.com/SatelliteQE","followers_url":"https://api.github.com/users/SatelliteQE/followers","following_url":"https://api.github.com/users/SatelliteQE/following{/other_user}","gists_url":"https://api.github.com/users/SatelliteQE/gists{/gist_id}","starred_url":"https://api.github.com/users/SatelliteQE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SatelliteQE/subscriptions","organizations_url":"https://api.github.com/users/SatelliteQE/orgs","repos_url":"https://api.github.com/users/SatelliteQE/repos","events_url":"https://api.github.com/users/SatelliteQE/events{/privacy}","received_events_url":"https://api.github.com/users/SatelliteQE/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SatelliteQE/nailgun","description":"Why use a hammer when you can use a nailgun.","fork":false,"url":"https://api.github.com/repos/SatelliteQE/nailgun","forks_url":"https://api.github.com/repos/SatelliteQE/nailgun/forks","keys_url":"https://api.github.com/repos/SatelliteQE/nailgun/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SatelliteQE/nailgun/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SatelliteQE/nailgun/teams","hooks_url":"https://api.github.com/repos/SatelliteQE/nailgun/hooks","issue_events_url":"https://api.github.com/repos/SatelliteQE/nailgun/issues/events{/number}","events_url":"https://api.github.com/repos/SatelliteQE/nailgun/events","assignees_url":"https://api.github.com/repos/SatelliteQE/nailgun/assignees{/user}","branches_url":"https://api.github.com/repos/SatelliteQE/nailgun/branches{/branch}","tags_url":"https://api.github.com/repos/SatelliteQE/nailgun/tags","blobs_url":"https://api.github.com/repos/SatelliteQE/nailgun/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SatelliteQE/nailgun/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SatelliteQE/nailgun/git/refs{/sha}","trees_url":"https://api.github.com/repos/SatelliteQE/nailgun/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SatelliteQE/nailgun/statuses/{sha}","languages_url":"https://api.github.com/repos/SatelliteQE/nailgun/languages","stargazers_url":"https://api.github.com/repos/SatelliteQE/nailgun/stargazers","contributors_url":"https://api.github.com/repos/SatelliteQE/nailgun/contributors","subscribers_url":"https://api.github.com/repos/SatelliteQE/nailgun/subscribers","subscription_url":"https://api.github.com/repos/SatelliteQE/nailgun/subscription","commits_url":"https://api.github.com/repos/SatelliteQE/nailgun/commits{/sha}","git_commits_url":"https://api.github.com/repos/SatelliteQE/nailgun/git/commits{/sha}","comments_url":"https://api.github.com/repos/SatelliteQE/nailgun/comments{/number}","issue_comment_url":"https://api.github.com/repos/SatelliteQE/nailgun/issues/comments{/number}","contents_url":"https://api.github.com/repos/SatelliteQE/nailgun/contents/{+path}","compare_url":"https://api.github.com/repos/SatelliteQE/nailgun/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SatelliteQE/nailgun/merges","archive_url":"https://api.github.com/repos/SatelliteQE/nailgun/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SatelliteQE/nailgun/downloads","issues_url":"https://api.github.com/repos/SatelliteQE/nailgun/issues{/number}","pulls_url":"https://api.github.com/repos/SatelliteQE/nailgun/pulls{/number}","milestones_url":"https://api.github.com/repos/SatelliteQE/nailgun/milestones{/number}","notifications_url":"https://api.github.com/repos/SatelliteQE/nailgun/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SatelliteQE/nailgun/labels{/name}","releases_url":"https://api.github.com/repos/SatelliteQE/nailgun/releases{/id}","created_at":"2014-12-11T19:39:59Z","updated_at":"2015-07-20T21:58:58Z","pushed_at":"2015-07-23T17:40:47Z","git_url":"git://github.com/SatelliteQE/nailgun.git","ssh_url":"git@github.com:SatelliteQE/nailgun.git","clone_url":"https://github.com/SatelliteQE/nailgun.git","svn_url":"https://github.com/SatelliteQE/nailgun","homepage":null,"size":1587,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"open_issues_count":26,"forks":13,"open_issues":26,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157"},"html":{"href":"https://github.com/SatelliteQE/nailgun/pull/157"},"issue":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/issues/157"},"comments":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/issues/157/comments"},"review_comments":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157/comments"},"review_comment":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/pulls/157/commits"},"statuses":{"href":"https://api.github.com/repos/SatelliteQE/nailgun/statuses/a45b32557da000e801ce6905f06ee2326a174992"}}}}