Repository: PyGithub/PyGithub

Issue:
Support updating SourceImport to get progress
When creating a SourceImport, the object returned has a `update` method, but calling it fails because of not setting the correct headers to enable the preview feature. It's useful to update the object, to get the progress of the import in case of a large source repository.

```
>>> u
AuthenticatedUser(login="mortenlj")
>>> r = u.create_repo("test-repo", "repo to test source import from API", private=True, has_issues=False, has_wiki=False, has_downloads=False, has_projects=False)
>>> si = r.create_source_import("hg", "https://url.to.repo", "mortenlj", "token")
>>> si
SourceImport(vcs_url="https://url.to.repo", url="https://api.github.com/repos/mortenlj/test-repo/import", status="importing", repository_url="https://api.github.com/repos/mortenlj/test-repo")
>>> si.update()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/mortenlj/.virtualenvs/fb49c46198b4854/lib/python3.6/site-packages/github/GithubObject.py", line 301, in update
    headers, data = self._requester._Requester__check(status, responseHeaders, output)
  File "/home/mortenlj/.virtualenvs/fb49c46198b4854/lib/python3.6/site-packages/github/Requester.py", line 279, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.GithubException: 415 {"message": "If you would like to help us test the Source Import API during its preview period, you must specify a custom media type in the 'Accept' header. Please see the docs for full details.", "documentation_url": "https://developer.github.com/v3/migrations/source_imports/#get-import-progress"}
```



Discussion:

Resolving patch:
diff --git a/github/GithubObject.py b/github/GithubObject.py
--- a/github/GithubObject.py
+++ b/github/GithubObject.py
@@ -279,7 +279,7 @@ def __complete(self):
         self._storeAndUseAttributes(headers, data)
         self.__completed = True
 
-    def update(self):
+    def update(self, additional_headers=None):
         '''
         Check and update the object with conditional request
         :rtype: Boolean value indicating whether the object is changed
@@ -289,6 +289,8 @@ def update(self):
             conditionalRequestHeader[Consts.REQ_IF_NONE_MATCH] = self.etag
         if self.last_modified is not None:
             conditionalRequestHeader[Consts.REQ_IF_MODIFIED_SINCE] = self.last_modified
+        if additional_headers is not None:
+            conditionalRequestHeader.update(additional_headers)
 
         status, responseHeaders, output = self._requester.requestJson(
             "GET",
diff --git a/github/SourceImport.py b/github/SourceImport.py
--- a/github/SourceImport.py
+++ b/github/SourceImport.py
@@ -23,6 +23,7 @@
 ################################################################################
 
 from __future__ import absolute_import
+from github import Consts
 import github.GithubObject
 
 
@@ -143,6 +144,10 @@ def vcs_url(self):
         self._completeIfNotSet(self._vcs_url)
         return self._vcs_url.value
 
+    def update(self):
+        import_header = {"Accept": Consts.mediaTypeImportPreview}
+        return super(SourceImport, self).update(additional_headers=import_header)
+
     def _initAttributes(self):
         self._authors_count = github.GithubObject.NotSet
         self._authors_url = github.GithubObject.NotSet


Test patch:
diff --git a/tests/ReplayData/SourceImport.testUpdate.txt b/tests/ReplayData/SourceImport.testUpdate.txt
new file mode 100644
--- /dev/null
+++ b/tests/ReplayData/SourceImport.testUpdate.txt
@@ -0,0 +1,10 @@
+https
+GET
+api.github.com
+None
+/repos/brix4dayz/source-import-test/import
+{'Authorization': 'Basic login_and_password_removed', 'Accept': 'application/vnd.github.barred-rock-preview', 'User-Agent': 'PyGithub/Python', 'If-None-Match': '"8659af05bfc77665551bec8f8a6bb2ce"'}
+None
+200
+[('content-length', '533'), ('x-runtime-rack', '0.137115'), ('vary', 'Accept, Authorization, Cookie, X-GitHub-OTP'), ('x-oauth-scopes', 'repo'), ('x-xss-protection', '1; mode=block'), ('x-content-type-options', 'nosniff'), ('x-accepted-oauth-scopes', ''), ('etag', '"8659af05bfc77665551bec8f8a6bb2ce"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('status', '200 OK'), ('x-ratelimit-remaining', '4987'), ('x-github-media-type', 'github.barred-rock-preview'), ('access-control-expose-headers', 'ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval'), ('x-github-request-id', 'D34D:2057:22D5E83:4403573:5A374787'), ('date', 'Mon, 18 Dec 2017 04:43:51 GMT'), ('access-control-allow-origin', '*'), ('content-security-policy', "default-src 'none'"), ('strict-transport-security', 'max-age=31536000; includeSubdomains; preload'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('x-frame-options', 'deny'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1513572653')]
+{"vcs":"mercurial","use_lfs":"undecided","vcs_url":"https://bitbucket.org/hfuss/source-import-test","status":"complete","status_text":"Done","has_large_files":false,"large_files_size":0,"large_files_count":0,"authors_count":1,"url":"https://api.github.com/repos/brix4dayz/source-import-test/import","html_url":"https://github.com/brix4dayz/source-import-test/import","authors_url":"https://api.github.com/repos/brix4dayz/source-import-test/import/authors","repository_url":"https://api.github.com/repos/brix4dayz/source-import-test"}
diff --git a/tests/SourceImport.py b/tests/SourceImport.py
--- a/tests/SourceImport.py
+++ b/tests/SourceImport.py
@@ -50,3 +50,9 @@ def testAttributes(self):
 
         self.assertEqual(self.source_import.__repr__(),
                          'SourceImport(vcs_url="https://bitbucket.org/hfuss/source-import-test", url="https://api.github.com/repos/brix4dayz/source-import-test/import", status="complete", repository_url="https://api.github.com/repos/brix4dayz/source-import-test")')
+
+    def testUpdate(self):
+        # The real test is that update() method passes the header
+        update_ret = self.source_import.update()
+        self.assertTrue(update_ret)
+        self.assertEqual(self.source_import.status, "complete")