Skip to content

Commit

Permalink
Implementation of the login page through gihub, the installation sele…
Browse files Browse the repository at this point in the history
…ction page through github as well and of the webhook handler to update the mongo db on each commit to master
  • Loading branch information
Pugnet committed Jun 29, 2019
1 parent 04663a7 commit bfff63c
Show file tree
Hide file tree
Showing 32 changed files with 760 additions and 263 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ __pycache__/
node_modules
.idea
*.lock
*.pyc
*.pyc
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ SHELL := /bin/bash
.PHONY: backend
backend:
pip install -r backend/requirements.txt
python3 backend/web_server.py
python3 backend/server.py

.PHONY: frontend
frontend:
yarn --cwd frontend install
yarn --cwd frontend run serve

.PHONY: webhook
webhook:
smee --url https://smee.io/UUkxjK9NwrD3pnTH --path /webhook_handler --port 5000

.PHONY: db
db:
mongod --config /usr/local/etc/mongod.conf
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ To clean the database document collection, run
make db_clean
```

### Setting the webhook redirect

To set the webhook redirect to localhost:
```bash
make webhook
```

#### Check out the app

Visit http://localhost:8080/
Expand All @@ -70,8 +77,26 @@ pip freeze > backend/requirements.txt
pip install -r backend/requirements.txt
```

### Clone the test folder locally

```bash
git clone git@github.com:codersdoc/Test.git
```

### Change the setup of the Github app

Go to this [link](https://github.com/settings/apps/tech-documentation).

### Useful links

* [Github enpoints available](https://developer.github.com/v3/apps/available-endpoints/).


## Miscallaneous

### Tech and library used

* pyGithub
* mongo
* flask
* Vuejs
4 changes: 3 additions & 1 deletion backend/github_interface/git/git_diff_types/git_diff_hunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def count_line_change_after_exclusive(self, line_number):
def __count_line_change_helper(self, line_number, is_count_before):
current_old_line_number = self.get_old_start_line() - 1
total_line_change = 0
counter_reseted = False

for code_line in self.code_lines:
if code_line.state == GitDiffCodeLineState.UNCHANGED:
Expand All @@ -55,10 +56,11 @@ def __count_line_change_helper(self, line_number, is_count_before):
elif code_line.state == GitDiffCodeLineState.ADDED:
total_line_change += 1

if current_old_line_number == line_number:
if current_old_line_number == line_number and not counter_reseted:
if is_count_before:
break
else:
counter_reseted = True
total_line_change = 0

return total_line_change
Expand Down
20 changes: 14 additions & 6 deletions backend/github_interface/github_types/github_commit_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@


class GithubCommitFile(GithubFile):
def __init__(self, content_file, previous_path, patch):
def __init__(self, content_file, path, previous_path, patch, is_deleted):
GithubFile.__init__(self, content_file)
self._previous_path = previous_path
self.__path = path
self.__has_path_changed = not (previous_path is None)
self.__previous_path = self.__path if previous_path is None else previous_path
self.__git_diff_parser = GitDiffParser(patch)
self.__is_deleted = is_deleted

@property
def previous_path(self):
return self._previous_path
return self.__previous_path

@property
def has_path_changed(self):
return self.__has_path_changed

@property
def is_deleted(self):
return self.__is_deleted

def calculate_updated_line_range(self, start_line, end_line):
return self.__git_diff_parser.calculate_updated_line_range(start_line, end_line)

def has_path_changed(self):
return self.previous_path is not None

def to_json(self):
new_json = {
"previous_path": self.previous_path
Expand Down
4 changes: 3 additions & 1 deletion backend/github_interface/github_types/github_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ def get_commit_files(self, branch_name="master", sha=None):
for file in commit.files:
if file.status == "removed":
file_object = None
is_deleted = True
else:
file_object = self.__get_file_object(file.filename)
files.append(GithubCommitFile(file_object, file.previous_filename, file.patch))
is_deleted = False
files.append(GithubCommitFile(file_object, file.filename, file.previous_filename, file.patch, is_deleted))

return files

Expand Down
32 changes: 0 additions & 32 deletions backend/github_interface/hooks/hook_manager.py

This file was deleted.

81 changes: 70 additions & 11 deletions backend/github_interface/interface.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,84 @@
from github import Github
import hmac

import requests
from github import Github, GithubIntegration
from hashlib import sha1

from github_interface.github_types.github_repository import GithubRepository
from mongo.credentials import CredentialsManager
from utils.constants import GITHUB_APP_IDENTIFIER


class GithubInterface:

def __init__(self, access_token):
self.__github_account = Github(access_token)
self.__repo_cache = {}
__repo_cache = {}

# TODO: use repo id and not repo name for the cache
@staticmethod
def get_repo(repo_name, is_user_access_token=False):
github_account = Github(GithubInterface.__fetch_access_token_from_db(is_user_access_token))

def get_repo(self, repo_name):
if repo_name in self.__repo_cache:
repo = self.__repo_cache[repo_name]
if repo_name in GithubInterface.__repo_cache:
repo = GithubInterface.__repo_cache[repo_name]
else:
self.__repo_cache[repo_name] = GithubRepository(self.__github_account.get_repo(repo_name))
repo = self.__repo_cache[repo_name]
GithubInterface.__repo_cache[repo_name] = GithubRepository(github_account.get_repo(repo_name))
repo = GithubInterface.__repo_cache[repo_name]
return repo

def get_repos(self):
@staticmethod
def get_repos(is_user_access_token=False):
github_account = Github(GithubInterface.__fetch_access_token_from_db(is_user_access_token))

repos = []
for repo in self.__github_account.get_user().get_repos():

if is_user_access_token:
raw_repos = github_account.get_user().get_repos()
else:
raw_repos = github_account.get_installation(-1).get_repos()

for repo in raw_repos:
repos.append(GithubRepository(repo))
return repos

@staticmethod
def get_user_access_token(client_id, client_secret, code, redirect_uri):
params = {
"client_id": client_id,
"client_secret": client_secret,
"code": code,
"redirect_uri": redirect_uri
}
r = requests.get(url="https://github.com/login/oauth/access_token", params=params)
content = r.content.decode("utf-8")
user_access_token = content[content.find("access_token=") + 13: content.find("&")]

return user_access_token

@staticmethod
def get_user_installations():
access_token = GithubInterface.__fetch_access_token_from_db(True)
response = requests.get(url="https://api.github.com/user/installations",
headers={
"Authorization": "token " + access_token,
"Accept": "application/vnd.github.machine-man-preview+json"
})
return response.json()

@staticmethod
def get_installation_access_token(installation_id, private_key):
integration = GithubIntegration(str(GITHUB_APP_IDENTIFIER), private_key)
return integration.get_access_token(installation_id).token

@staticmethod
def verify_signature(signature, body, github_webhook_secret):
computed_signature = "sha1=" + hmac.new(str.encode(github_webhook_secret), body, sha1).hexdigest()
return computed_signature == signature

@staticmethod
def __fetch_access_token_from_db(is_user_access_token):
if is_user_access_token:
return CredentialsManager.read_credentials()["user_access_token"]
else:
return CredentialsManager.read_credentials()["installation_access_token"]


29 changes: 29 additions & 0 deletions backend/mongo/credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import json


class CredentialsManager:

@staticmethod
def write_credentials(user_access_token="", installation_access_token=""):
credentials = CredentialsManager.read_credentials()

user_access_token = credentials[
"user_access_token"] if "user_access_token" in credentials and not user_access_token else user_access_token
installation_access_token = credentials[
"installation_access_token"] if "installation_access_token" in credentials and not installation_access_token else installation_access_token

with open("backend/ressources/credentials.txt", "w") as file:
credentials = "{\"installation_access_token\":\"" + str(
installation_access_token) + "\",\"user_access_token\":\"" + str(user_access_token) + "\"}"
file.write(credentials)
file.close()

@staticmethod
def read_credentials():
with open("backend/ressources/credentials.txt", "r") as file:
file_content = file.read()
if file_content == "":
file_content = "{}"
file.close()

return json.loads(file_content)
46 changes: 44 additions & 2 deletions backend/mongo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ class FileReference:
A FileReference is part of a Document, and references lines of code in repositories
"""

def __init__(self, ref_id, repo, path, start_line, end_line):
def __init__(self, ref_id, repo, path, start_line, end_line, is_deleted):
self.ref_id = ref_id
self.repo = repo
self.path = path
self.start_line = start_line
self.end_line = end_line
self.is_deleted = is_deleted

def to_json(self):
return {
Expand All @@ -27,6 +28,7 @@ def to_json(self):
'path': self.path,
'start_line': self.start_line,
'end_line': self.end_line,
'is_deleted': self.is_deleted
}

@staticmethod
Expand All @@ -36,7 +38,8 @@ def from_json(file_ref):
file_ref['repo'],
file_ref['path'],
int(file_ref['start_line']),
int(file_ref['end_line'])
int(file_ref['end_line']),
file_ref['is_deleted']
)

def __init__(self, name, content, references):
Expand All @@ -47,6 +50,45 @@ def __init__(self, name, content, references):
def insert(self):
return self.COLLECTION.insert_one(self.to_json())

@staticmethod
def __update(query, new_values):
return Document.COLLECTION.update_one(query, new_values)

@staticmethod
def update_lines_ref(ref_id, new_start_line, new_end_line):
query = { "refs.ref_id": ref_id }
new_values = {
"$set": {
"refs.$.start_line": new_start_line,
"refs.$.end_line": new_end_line
}
}

return Document.__update(query, new_values)

@staticmethod
def update_path_ref(ref_id, path):
query = {"refs.ref_id": ref_id}
new_values = {
"$set": {
"refs.$.path": path
}
}

return Document.__update(query, new_values)

staticmethod

def update_is_deleted_ref(ref_id, is_deleted):
query = {"refs.ref_id": ref_id}
new_values = {
"$set": {
"refs.$.is_deleted": is_deleted
}
}

return Document.__update(query, new_values)

@staticmethod
def find(name):
doc = Document.COLLECTION.find_one({
Expand Down
Loading

1 comment on commit bfff63c

@tech-documentation
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit has affected the following documentation files:

Please sign in to comment.