前回まで
http://ytyaru.hatenablog.com/entry/2017/09/30/000000
http://ytyaru.hatenablog.com/entry/2017/10/01/000000
- 重複コードの解消
- GitHubAPI部分の別ファイル実装
http://ytyaru.hatenablog.com/entry/2017/10/01/000000
- Data.pyを共通化
今回
- GitHubAPIとアプリ機能を別ファイルに実装したい
以下のような感じにざっくり分ける。テキトーなのであとで変更するかもしれない。
- GitHubUploader
使っているAPI網羅
- uploader/command/repository/Commiter.py
- uploader/command/repository/Creator.py
- uploader/command/repository/Deleter.py
- uploader/command/repository/Editor.py
- patch ‘https://api.github.com/repos/{0}/{1}’
- database/src/license/insert/command/miscellaneous/Licenses.py
- get ‘https://api.github.com/licenses’
- get ‘https://api.github.com/licenses/’ + key
- database/src/other_repo/insert/github/miscellaneous/Licenses.py
- get ‘https://api.github.com/licenses’
- get ‘https://api.github.com/licenses/’ + key
- get ‘https://api.github.com/repos/{0}/{1}’
- get ‘https://api.github.com/repos/{0}/{1}/languages’
- database/src/other_repo/insert/github/miscellaneous/license/Requester.py
- get ‘https://api.github.com/licenses’
- get ‘https://api.github.com/licenses/’ + key
- get ‘https://api.github.com/repos/{0}/{1}’
実装
def __GetLanguages(self): url = 'https://api.github.com/repos/{0}/{1}/languages'.format(self.data.get_username(), self.data.get_repo_name()) r = requests.get(url) if 300 <= r.status_code: print(r.status_code) print(r.text) print(url) raise Exception("HTTP Error {0}".format(r.status_code)) return None else: print(r.text) return json.loads(r.text)
def __CreateRemoteRepository(self): url = 'https://api.github.com/user/repos' post_data = json.dumps({"name": self.data.get_repo_name(), "description": self.data.get_repo_description(), "homepage": self.data.get_repo_homepage()}) headers={ "Time-Zone": "Asia/Tokyo", "Authorization": "token {0}".format(self.data.get_access_token(['repo'])) } r = requests.post(url, data=post_data, headers=headers) print(r.text) time.sleep(2) return json.loads(r.text)
def __DeleteRemoteRepository(self): url = 'https://api.github.com/repos/{0}/{1}'.format(self.data.get_username(), self.data.get_repo_name()) headers={ "Time-Zone": "Asia/Tokyo", "Authorization": "token {0}".format(self.data.get_access_token(['delete_repo'])) } r = requests.delete(url, headers=headers) if 204 != r.status_code: raise Exception('HTTPエラー: {0}'.format(status_code)) time.sleep(2)
def __EditRemoteRepository(self, name, description, homepage): # リポジトリ名は必須 url = 'https://api.github.com/repos/{0}/{1}'.format(self.data.get_username(), self.data.get_repo_name()) headers={ "Time-Zone": "Asia/Tokyo", "Authorization": "token {0}".format(self.data.get_access_token()) } data = {} data['name'] = name if not(None is description or '' == description): data['description'] = description if not(None is homepage or '' == homepage): data['homepage'] = homepage r = requests.patch(url, headers=headers, data=json.dumps(data)) if 200 != r.status_code: raise Exception('HTTPエラー: {0}'.format(r.status_code)) time.sleep(2) return json.loads(r.text)
def __RequestLicenses(self): licenses = [] url = 'https://api.github.com/licenses' r = requests.get(url, headers=self.__GetHttpHeaders()) licenses += self.__ReturnResponse(r, success_code=200) next = self.page.get_next(r) while (None is not next): r = requests.get(next, headers=self.__GetHttpHeaders()) licenses += self.__ReturnResponse(r, success_code=200) next = self.page.get_next(r) return licenses def __RequestLicense(self, key): url = 'https://api.github.com/licenses/' + key r = requests.get(url, headers=self.__GetHttpHeaders()) return self.__ReturnResponse(r, success_code=200) def __GetHttpHeaders(self): return { "Accept": "application/vnd.github.drax-preview+json", "Time-Zone": "Asia/Tokyo", "Authorization": "token {0}".format(self.data.get_access_token()) } def __ReturnResponse(self, r, success_code=None, sleep_time=2, is_show=True): if is_show: print("HTTP Status Code: {0}".format(r.status_code)) print(r.text) time.sleep(sleep_time) if None is not success_code: if (success_code != r.status_code): raise Exception('HTTP Error: {0}'.format(r.status_code)) return None return json.loads(r.text) def __BoolToInt(self, bool_value): if True == bool_value: return 1 else: return 0 def __ArrayToString(self, array): ret = "" for v in array: ret = v + ',' return ret[:-1]
HTTPの処理
- Request
- Response
階層わけ
分類
- HTTP通信
- pagenation
- 型変換
- TEXT:
r.text
- JSON:
json.loads(r.text)
- バイナリ:
r.content
- TEXT:
- GitHubAPI仕様(v3)
- SQLiteインタフェース(形式)
- 形式変換
- カンマ区切り文字列←→配列
- 0,1←→True,False
- 形式変換
- Pythonインタフェース(関数、try-catch)
- ログ
* 例外時の対処 * 標準出力 * ファイル出力
- アプリケーション機能
APIエンドポイント
https://api.github.com/
をブラウザのロケールバーから実行した結果。
{ "current_user_url": "https://api.github.com/user", "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}", "authorizations_url": "https://api.github.com/authorizations", "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}", "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}", "emails_url": "https://api.github.com/user/emails", "emojis_url": "https://api.github.com/emojis", "events_url": "https://api.github.com/events", "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}", "issues_url": "https://api.github.com/issues", "keys_url": "https://api.github.com/user/keys", "notifications_url": "https://api.github.com/notifications", "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}", "organization_url": "https://api.github.com/orgs/{org}", "public_gists_url": "https://api.github.com/gists/public", "rate_limit_url": "https://api.github.com/rate_limit", "repository_url": "https://api.github.com/repos/{owner}/{repo}", "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}", "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}", "starred_url": "https://api.github.com/user/starred{/owner}{/repo}", "starred_gists_url": "https://api.github.com/gists/starred", "team_url": "https://api.github.com/teams", "user_url": "https://api.github.com/users/{user}", "user_organizations_url": "https://api.github.com/user/orgs", "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}", "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}" }
GitHub API v3 | GitHub Developer Guide
上記の仕様書にはall
(全部)とあったが、全部ではない。たとえばhttps://api.github.com/licenses
がない。
ライセンスAPIといい、なぜ「all(全部)」という嘘をつくのか。それともHTTPヘッダのAcceptにAPIのバージョンを指定すると変化するのだろうか。
API制約
APIごとに異なるもの。APIごとの差異をうまく隠蔽するにはどうしたらいいか。具体的に見えない。とくにParameter。
- Endpoint(URL)
- HttpMethod
- 正常HTTPコード
- Parameter
- Header
HTTPリクエスト引数
parameterの設定。これが面倒そう。共通化できるのか。どういうインタフェースになるのか。具体的に見えない。
- 名前
- 型
- object
- array
- string
- integer
- boolean
- 制約
- 必須
- check(いずれかの値or複合or特定の組合せのみ)
- default
- キーなし
- None
- 指定値
所感
まとめられるだろうか。