Skip to content

Commit

Permalink
Change response of register endpoint to add scan report details.
Browse files Browse the repository at this point in the history
Summary of changes:
1. Modify /register endpoint as per new swagger specification.
2. Remove the test(s) related to /register endpoint since they are more suitable for integration test suite.
  • Loading branch information
abs51295 committed Mar 26, 2018
1 parent 06f1278 commit 38b1a9c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 53 deletions.
91 changes: 75 additions & 16 deletions src/rest_api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Definition of the routes for gemini server."""
import flask
from flask import Flask, request
from flask import Flask, request, current_app
from flask_cors import CORS
from utils import DatabaseIngestion, scan_repo, validate_request_data
from utils import DatabaseIngestion, scan_repo, validate_request_data, retrieve_worker_result
from f8a_worker.setup_celery import init_selinon

app = Flask(__name__)
Expand Down Expand Up @@ -34,15 +34,14 @@ def scan():
@app.route('/api/v1/register', methods=['POST'])
def register():
"""
Endpoint for registering a new repositor.
Endpoint for registering a new repository.
Registers new information and
updates existing repo information.
"""
resp_dict = {
"data": [],
"success": True,
"summary": "{} successfully registered"
"summary": ""
}
input_json = request.get_json()
if request.content_type != 'application/json':
Expand All @@ -55,21 +54,81 @@ def register():
resp_dict["success"] = False
resp_dict["summary"] = validated_data[1]
return flask.jsonify(resp_dict), 404

try:
status = DatabaseIngestion.store_record(input_json)
resp_dict["data"] = status
except Exception as e:
repo_info = DatabaseIngestion.get_info(input_json.get('git-url'))
if repo_info.get('is_valid'):
data = repo_info.get('data')
git_sha = data["git_sha"]
# Update the record to reflect new git_sha if any.
DatabaseIngestion.update_data(input_json)
else:
try:
DatabaseIngestion.store_record(input_json)
status = scan_repo(input_json)
if status is not True:
resp_dict["success"] = False
resp_dict["summary"] = "New Repo Scan Initialization Failure"
return flask.jsonify(resp_dict), 500

resp_dict["summary"] = "Repository {} with commit-hash {} " \
"has been successfully registered. " \
"Please check back for report after some time." \
.format(input_json.get('git-url'),
input_json.get('git-sha'))

return flask.jsonify(resp_dict), 200
except Exception as e:
resp_dict["success"] = False
resp_dict["summary"] = "Database Ingestion Failure due to: {}" \
.format(e)
return flask.jsonify(resp_dict), 500
except Exception:
resp_dict["success"] = False
resp_dict["summary"] = "Database Ingestion Failure due to" + str(e)
resp_dict["summary"] = "Cannot get information about repository {}" \
.format(input_json.get('git-url'))
return flask.jsonify(resp_dict), 500

status = scan_repo(input_json)
if status is not True:
resp_dict["success"] = False
resp_dict["summary"] = "New Repo Scan Initializtion Failure"
return flask.jsonify(resp_dict), 500
rep_summary = resp_dict["summary"].format(input_json['git-url'])
resp_dict["summary"] = rep_summary
# Checks if data is defined and compares new git_sha with old.
is_new_commit_hash = input_json.get("git-sha") != data["git_sha"]

worker_result = retrieve_worker_result(git_sha, "ReportGenerationTask")
if worker_result:
resp_dict["summary"] = "Repository {} with commit-hash {} " \
"has already been registered and scanned. " \
"Please check the scan report for details. " \
.format(input_json.get('git-url'),
input_json.get('git-sha'))

if is_new_commit_hash:
resp_dict["summary"] = "Repository {} with commit-hash {} " \
"has already been registered and scanned. " \
"Please check the scan report for details. " \
"You can check the report for commit-hash {} after" \
"some time." \
.format(input_json.get('git-url'),
data["git_sha"], input_json.get("git-sha"))

task_result = worker_result.get('task_result')
if task_result:
resp_dict.update({
"last_scanned_at": task_result.get('scanned_at'),
"last_scan_report": task_result.get('dependencies')
})
return flask.jsonify(resp_dict), 200
else:
resp_dict["success"] = False
resp_dict["summary"] = "Failed to retrieve scan report."
return flask.jsonify(resp_dict), 500

resp_dict.update({
"summary": "Repository {} was already registered, but no report for "
"commit-hash {} was found. Please check back later."
.format(input_json.get('git-url'), git_sha),
"last_scanned_at": data['last_scanned_at'],
"last_scan_report": None
})

return flask.jsonify(resp_dict), 200


Expand Down
66 changes: 45 additions & 21 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
"""Utility classes and functions."""
from flask import current_app
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from f8a_worker.models import OSIORegisteredRepos
from f8a_worker.models import OSIORegisteredRepos, WorkerResult
from f8a_worker.setup_celery import init_celery
from selinon import run_flow
import datetime
import requests
import os
import logging


logger = logging.getLogger(__name__)

GREMLIN_SERVER_URL_REST = "http://{host}:{port}".format(
Expand All @@ -31,7 +31,7 @@ class Postgres:
def __init__(self):
"""Postgres utility class constructor."""
con_string = 'postgresql://{user}' + ':{passwd}@{pg_host}:' \
+ '{pg_port}/{db}?sslmode=disable'
+ '{pg_port}/{db}?sslmode=disable'

self.connection = con_string.format(
user=os.getenv('POSTGRESQL_USER'),
Expand All @@ -56,6 +56,29 @@ def session(self):
_rdb = Postgres()


def retrieve_worker_result(external_request_id, worker):
"""Retrieve results for selected worker from RDB."""
start = datetime.datetime.now()
session = get_session()
try:
query = session.query(WorkerResult) \
.filter(WorkerResult.external_request_id == external_request_id,
WorkerResult.worker == worker)
result = query.one()
except (NoResultFound, MultipleResultsFound):
return None
except SQLAlchemyError:
session.rollback()
raise
result_dict = result.to_dict()
elapsed_seconds = (datetime.datetime.now() - start).total_seconds()
msg = "It took {t} seconds to retrieve {w} " \
"worker results for {r}.".format(t=elapsed_seconds, w=worker, r=external_request_id)
current_app.logger.debug(msg)

return result_dict


def get_session():
"""Retrieve the database connection session."""
try:
Expand Down Expand Up @@ -83,7 +106,7 @@ def get_session_retry(retries=3, backoff_factor=0.2,
def validate_request_data(input_json):
"""Validate the data.
:param data: dict, describing data
:param input_json: dict, describing data
:return: boolean, result
"""
validate_string = "{} cannot be empty"
Expand Down Expand Up @@ -112,25 +135,31 @@ def _to_object_dict(data):
return return_dict


class DatabaseIngestion():
class DatabaseIngestion:
"""Class to ingest data into Database."""

@staticmethod
def _update_data(session, data):
def update_data(data):
"""Update existing record in the database.
:param data: dict, describing github data
:return: None
"""
try:
entries = session.query(OSIORegisteredRepos).\
filter(OSIORegisteredRepos.git_url == data["git-url"]).\
session = get_session()
session.query(OSIORegisteredRepos). \
filter(OSIORegisteredRepos.git_url == data["git-url"]). \
update(_to_object_dict(data))
session.commit()
except NoResultFound:
raise Exception("Record trying to update doesnot exist")
raise Exception("Record trying to update does not exist")
except SQLAlchemyError:
session.rollback()
raise Exception("Error in updating data")

@classmethod
def store_record(cls, data):
"""Store new record and update old ones.
"""Store new record in the database.
:param data: dict, describing github data
:return: boolean based on completion of process
Expand All @@ -141,11 +170,6 @@ def store_record(cls, data):
raise Exception("github Url not found")
try:
session = get_session()
check_existing = cls.get_info(git_url)
if check_existing["is_valid"]:
cls._update_data(session, data)
return check_existing["data"]

entry = OSIORegisteredRepos(
git_url=data['git-url'],
git_sha=data['git-sha'],
Expand All @@ -165,7 +189,7 @@ def store_record(cls, data):
def get_info(cls, search_key):
"""Get information about github url.
:param search_key: github url to serach database
:param search_key: github url to search database
:return: record from database if exists
"""
if not search_key:
Expand All @@ -175,17 +199,17 @@ def get_info(cls, search_key):

try:
entry = session.query(OSIORegisteredRepos) \
.filter(OSIORegisteredRepos.git_url == search_key).one()
.filter(OSIORegisteredRepos.git_url == search_key).one()
except NoResultFound:
logger.info("No info for search_key '%s' was found", search_key)
return {'error': 'No information in the records', 'is_valid': False}
except SQLAlchemyError:
session.rollback()
raise Exception("Error in storing the record in current session")
raise Exception("Error in retrieving the record in current session")
except Exception as e:
raise {
'error': 'Error in getting info due to {}'.format(e),
'is_valid': False
'error': 'Error in getting info due to {}'.format(e),
'is_valid': False
}

return {'is_valid': True, 'data': entry.to_dict()}
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os

from flask import current_app
from f8a_worker.models import OSIORegisteredRepos
from f8a_worker.models import OSIORegisteredRepos, WorkerResult
from sqlalchemy import create_engine
from f8a_worker.models import Base
import pytest
Expand Down Expand Up @@ -35,3 +35,4 @@ def create_database():
engine = create_engine(connection)
Base.metadata.drop_all(engine)
OSIORegisteredRepos.__table__.create(engine)
WorkerResult.__table__.create(engine)
15 changes: 0 additions & 15 deletions tests/test_rest_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,3 @@ def test_liveness_endpoint(client):
response = client.get(api_route_for("liveness"))
assert response.status_code == 200
json_data = get_json_from_response(response)


def test_register_api_endpoint(client, mocker):
"""Test function for register endpoint."""
create_database()
scan_mock = mocker.patch("src.rest_api.scan_repo")
scan_mock.return_value = True
reg_resp = client.post(api_route_for("register"),
data=json.dumps(payload), content_type='application/json')
assert reg_resp.status_code == 200
jsn = get_json_from_response(reg_resp)
assert(jsn["success"])
assert(jsn['data']["data"]["git_sha"] == payload["git-sha"])
assert(jsn['data']["data"]["git_url"] == payload["git-url"])
assert(jsn['data']["data"]["email_ids"] == payload["email-ids"])

0 comments on commit 38b1a9c

Please sign in to comment.