From e8e878e62395ebe2b7f160513758e6bcd06dd135 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 9 Jul 2020 19:00:59 -0400 Subject: [PATCH] Add Github Navigator - Flask application --- examples/applications/ghnav-flask/config.yml | 6 ++ .../ghnav-flask/githubnavigator/__init__.py | 0 .../githubnavigator/application.py | 32 +++++++++ .../ghnav-flask/githubnavigator/entrypoint.py | 8 +++ .../ghnav-flask/githubnavigator/github.py | 67 +++++++++++++++++++ .../githubnavigator/templates/index.html | 40 +++++++++++ .../ghnav-flask/githubnavigator/views.py | 19 ++++++ .../ghnav-flask/githubnavigator/webapp.py | 16 +++++ .../applications/ghnav-flask/requirements.txt | 3 + 9 files changed, 191 insertions(+) create mode 100644 examples/applications/ghnav-flask/config.yml create mode 100644 examples/applications/ghnav-flask/githubnavigator/__init__.py create mode 100644 examples/applications/ghnav-flask/githubnavigator/application.py create mode 100644 examples/applications/ghnav-flask/githubnavigator/entrypoint.py create mode 100644 examples/applications/ghnav-flask/githubnavigator/github.py create mode 100644 examples/applications/ghnav-flask/githubnavigator/templates/index.html create mode 100644 examples/applications/ghnav-flask/githubnavigator/views.py create mode 100644 examples/applications/ghnav-flask/githubnavigator/webapp.py create mode 100644 examples/applications/ghnav-flask/requirements.txt diff --git a/examples/applications/ghnav-flask/config.yml b/examples/applications/ghnav-flask/config.yml new file mode 100644 index 00000000..4409662f --- /dev/null +++ b/examples/applications/ghnav-flask/config.yml @@ -0,0 +1,6 @@ +github: + auth_token: ${GITHUB_TOKEN} + request_timeout: 10 +search: + default_term: "Dependency Injector" + default_limit: 5 diff --git a/examples/applications/ghnav-flask/githubnavigator/__init__.py b/examples/applications/ghnav-flask/githubnavigator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/applications/ghnav-flask/githubnavigator/application.py b/examples/applications/ghnav-flask/githubnavigator/application.py new file mode 100644 index 00000000..a29072e4 --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/application.py @@ -0,0 +1,32 @@ +"""Application module.""" + +from dependency_injector import containers, providers + +from . import github, views, webapp + + +class Application(containers.DeclarativeContainer): + """Application container.""" + + config = providers.Configuration() + + github_client = providers.Factory( + github.GitHubApiClient, + auth_token=config.github.auth_token, + request_timeout=config.github.request_timeout, + ) + + index_view = providers.Callable( + views.index, + github_client=github_client, + default_search_term=config.search.default_term, + default_search_limit=config.search.default_limit, + ) + + app = providers.Factory( + webapp.create_app, + name=__name__, + routes=[ + webapp.Route('/', 'index', index_view, methods=['GET']), + ], + ) diff --git a/examples/applications/ghnav-flask/githubnavigator/entrypoint.py b/examples/applications/ghnav-flask/githubnavigator/entrypoint.py new file mode 100644 index 00000000..fb545b90 --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/entrypoint.py @@ -0,0 +1,8 @@ +"""Entrypoint module.""" + +from .application import Application + + +application = Application() +application.config.from_yaml('config.yml') +app = application.app() diff --git a/examples/applications/ghnav-flask/githubnavigator/github.py b/examples/applications/ghnav-flask/githubnavigator/github.py new file mode 100644 index 00000000..18593347 --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/github.py @@ -0,0 +1,67 @@ +"""Github API module.""" + +import requests + + +class GitHubApiClient: + """GitHub API client performs operations with Github API.""" + + API_URL = 'https://api.github.com/' + DEFAULT_LIMIT = 5 + + def __init__(self, auth_token, request_timeout): + """Initialize search.""" + self._auth_token = auth_token + self._request_timeout = request_timeout + + def search_repositories(self, search_term, limit): + """Search repositories.""" + if not search_term: + return [] + + repositories = self._make_search('repositories', search_term, limit) + latest_commits = [ + self._get_latest_commit(repository, search_term) + for repository in repositories + ] + return list(zip(repositories, latest_commits)) + + def _make_search(self, entity, search_term, limit): + headers = {} + if self._auth_token: + headers['authorization'] = f'token {self._auth_token}' + + response = requests.get( + url=f'{self.API_URL}search/{entity}', + params={ + 'q': f'{search_term} in:name', + 'sort': 'updated', + 'order': 'desc', + 'page': 1, + 'per_page': limit, + }, + headers=headers, + timeout=self._request_timeout, + ) + data = response.json() + return data['items'] + + def _get_latest_commit(self, repository, search_term): + headers = {} + if self._auth_token: + headers['authorization'] = f'token {self._auth_token}' + + response = requests.get( + url=repository['commits_url'].replace('{/sha}', ''), + params={ + 'q': f'{search_term} in:name', + 'sort': 'updated', + 'order': 'desc', + 'page': 1, + 'per_page': 1, + }, + headers=headers, + timeout=self._request_timeout, + ) + data = response.json() + return data[0] diff --git a/examples/applications/ghnav-flask/githubnavigator/templates/index.html b/examples/applications/ghnav-flask/githubnavigator/templates/index.html new file mode 100644 index 00000000..c7e878ba --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/templates/index.html @@ -0,0 +1,40 @@ + + + + Github Navigator + + +

Github Navigator

+
+

+ Search term: + Limit: + +

+
+

Search results

+{% if repositories|length == 0 %} +No search results +{% endif %} +{% for repository, latest_commit in repositories %} {{n}} +

+ Search result # {{ loop.index }} from {{ repositories|length }} +

+

+ Repository: {{ repository['name'] }} +

+

+ Repository owner: + avatar + {{ repository['owner']['login'] }} +

+

+ Created at: {{ repository['created_at'] }} +

+

+ LastCommit: {{ latest_commit['sha'] }} {{ latest_commit['commit']['message'] }} {{ latest_commit['commit']['author']['name'] }} +

+
+{% endfor %} + + diff --git a/examples/applications/ghnav-flask/githubnavigator/views.py b/examples/applications/ghnav-flask/githubnavigator/views.py new file mode 100644 index 00000000..b3fdf06a --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/views.py @@ -0,0 +1,19 @@ +"""Views module.""" + +from flask import request, render_template + +from .github import GitHubApiClient + + +def index(github_client: GitHubApiClient, default_search_term, default_search_limit): + search_term = request.args.get('search_term', default_search_term) + limit = request.args.get('limit', default_search_limit) + + repositories = github_client.search_repositories(search_term, limit) + + return render_template( + 'index.html', + search_term=search_term, + limit=limit, + repositories=repositories, + ) diff --git a/examples/applications/ghnav-flask/githubnavigator/webapp.py b/examples/applications/ghnav-flask/githubnavigator/webapp.py new file mode 100644 index 00000000..e29f4746 --- /dev/null +++ b/examples/applications/ghnav-flask/githubnavigator/webapp.py @@ -0,0 +1,16 @@ +"""Web application module.""" + +from flask import Flask + + +def create_app(name, routes): + app = Flask(name) + for route in routes: + app.add_url_rule(*route.args, **route.kwargs) + return app + + +class Route: + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs diff --git a/examples/applications/ghnav-flask/requirements.txt b/examples/applications/ghnav-flask/requirements.txt new file mode 100644 index 00000000..a430e9e9 --- /dev/null +++ b/examples/applications/ghnav-flask/requirements.txt @@ -0,0 +1,3 @@ +dependency-injector +flask +requests