Page MenuHomeSoftware Heritage

No OneTemporary

diff --git a/swh/web/ui/controller.py b/swh/web/ui/controller.py
index ba4d2d41..8b75990e 100755
--- a/swh/web/ui/controller.py
+++ b/swh/web/ui/controller.py
@@ -1,320 +1,326 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import logging
-from flask import redirect, render_template, url_for, flash, request
+from flask import redirect, render_template, url_for, flash, jsonify, request
from flask import make_response
from swh.core.hashutil import ALGORITHMS
from swh.web.ui.main import app
from swh.web.ui import service, query
hash_filter_keys = ALGORITHMS
@app.route('/')
def main():
"""Main application view.
At the moment, redirect to the content search view.
"""
return redirect(url_for('info'))
@app.route('/info')
def info():
"""A simple api to define what the server is all about.
"""
logging.info('Dev SWH UI')
return 'Dev SWH UI'
@app.route('/search')
def search():
"""Search for hashes in swh-storage.
"""
q = request.args.get('q', '')
if q:
flash("Search hash '%s' posted!" % q)
message = service.lookup_hash(q)
else:
message = ''
return render_template('search.html',
q=q,
message=message)
@app.route('/browse/revision/<sha1_git>')
def revision(sha1_git):
"""Show commit information.
Args:
sha1_git: the revision's sha1
Returns:
Revision information
"""
return render_template('revision.html',
sha1_git=sha1_git)
@app.route('/browse/directory/<sha1_git>')
def directory(sha1_git):
"""Show directory information.
Args:
sha1_git: the directory's sha1
Returns:
Directory information
"""
return render_template('directory.html',
sha1_git=sha1_git)
@app.route('/browse/directory/<sha1_git>/<path:p>')
def directory_at_path(sha1_git, p):
"""Show directory information for the sha1_git at path.
Args:
sha1_git: the directory's sha1
path: file or directory pointed to
Returns:
Directory information at sha1_git + path
"""
return render_template('directory.html',
sha1_git=sha1_git,
path=p)
@app.route('/browse/content/<hash>:<sha>')
def content(hash, sha):
"""Show content information.
Args:
hash: hash according to HASH_ALGO, where HASH_ALGO is
one of: sha1, sha1_git, sha256. This means that several different URLs (at
least one per HASH_ALGO) will point to the same content
sha: the sha with 'hash' format
Returns:
The content's information at sha1_git
"""
# Checks user input
if hash not in hash_filter_keys:
return make_response(
'Bad request, sha must be one of sha1, sha1_git, sha256',
400)
h = query.categorize_hash(sha)
if h == {}:
return make_response(
'Bad request, %s is not of type %s' % (sha, hash),
400)
if hash == 'sha256' and not h.get(hash):
return make_response(
'Bad request, %s is not of type sha256' % (sha,),
400)
if hash != 'sha256' and not h.get('sha1') and not h.get('sha1_git'):
return make_response(
'Bad request, %s is not of type sha1 or sha1_git' % (sha,),
400)
message = service.lookup_hash_origin(h)
return render_template('content.html',
hash=hash,
sha=sha,
message=message)
@app.route('/browse/release/<sha1_git>')
def release(sha1_git):
"""Show release's information.
Args:
sha1_git: sha1_git for this particular release
Returns:
Release's information
"""
return 'Release information at %s' % sha1_git
@app.route('/browse/person/<int:id>')
def person(id):
"""Show Person's information at id.
Args:
id: person's unique identifier
Returns:
Person's information
"""
return 'Person information at %s' % id
@app.route('/browse/origin/<int:id>')
def origin(id):
"""Show origin's information at id.
Args:
id: origin's unique identifier
Returns:
Origin's information
"""
return 'Origin information at %s' % id
@app.route('/browse/project/<int:id>')
def project(id):
"""Show project's information at id.
Args:
id: project's unique identifier
Returns:
Project's information
"""
return 'Project information at %s' % id
@app.route('/browse/organization/<int:id>')
def organization(id):
"""Show organization's information at id.
Args:
id: organization's unique identifier
Returns:
Organization's information
"""
return 'Organization information at %s' % id
@app.route('/browse/directory/<string:timestamp>/'
'<string:origin_type>+<path:origin_url>|/'
'<path:branch>|/<path:path>')
def directory_at_origin(timestamp, origin_type, origin_url, branch, path):
"""Show directory information at timestamp, origin-type, origin-url, branch
and path.
Those parameters are separated by the `|` terminator.
Args:
timestamp: the timestamp to look for. can be latest or some iso8601
date format. (TODO: decide the time matching policy.)
origin_type: origin's type
origin_url: origin's url (can contain `/`)
branch: branch name which can contain `/`
path: path to directory or file
Returns:
Directory information at the given parameters.
"""
return 'Directory at (%s, %s, %s, %s, %s)' % (timestamp,
origin_type,
origin_url,
branch,
path)
@app.route('/browse/revision/<string:timestamp>/'
'<string:origin_type>+<path:origin_url>|/<path:branch>')
def revision_at_origin_and_branch(timestamp, origin_type, origin_url, branch):
"""Show revision information at timestamp, origin, and branch.
Those parameters are separated by the `|` terminator.
Args:
timestamp: the timestamp to look for. can be latest or some iso8601
date format. (TODO: decide the time matching policy.)
origin_type: origin's type
origin_url: origin's url (can contain `/`)
branch: branch name which can contain /
Returns:
Revision information at the given parameters.
"""
return 'Revision at (ts=%s, type=%s, url=%s, branch=%s)' % (timestamp,
origin_type,
origin_url,
branch)
@app.route('/browse/revision/<string:timestamp>/'
'<string:origin_type>+<path:origin_url>|')
def revision_at_origin(timestamp, origin_type, origin_url):
"""Show revision information at timestamp, origin, and branch.
Those parameters are separated by the `|` terminator.
Args:
timestamp: the timestamp to look for. can be latest or iso8601
date
format. (TODO: decide the time matching policy.)
origin_type: origin's type
origin_url: origin's url (can contain `/`)
Returns:
Revision information at the given parameters.
"""
return 'Revision at (timestamp=%s, type=%s, url=%s)' % (timestamp,
origin_type,
origin_url)
+@app.route('/api/1/stat/counters')
+def api_stats():
+ """Return statistics as a JSON object"""
+ return jsonify(service.stat_counters())
+
+
def run(conf):
"""Run the api's server.
Args:
conf is a dictionary of keywords:
- 'db_url' the db url's access (through psycopg2 format)
- 'content_storage_dir' revisions/directories/contents storage on disk
- 'host' to override the default 127.0.0.1 to open or not the server
to the world
- 'port' to override the default of 5000 (from the underlying layer:
flask)
- 'debug' activate the verbose logs
- 'secret_key' the flask secret key
Returns:
Never
Raises:
?
"""
print("""SWH Web UI run
host: %s
port: %s
debug: %s""" % (conf['host'], conf.get('port', None), conf['debug']))
app.secret_key = conf['secret_key']
app.config.update({'conf': conf})
app.run(host=conf['host'],
port=conf.get('port', None),
debug=conf['debug'])
diff --git a/swh/web/ui/service.py b/swh/web/ui/service.py
index 558a3c18..e9674a87 100755
--- a/swh/web/ui/service.py
+++ b/swh/web/ui/service.py
@@ -1,75 +1,84 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
from swh.web.ui import main
from swh.web.ui import query
def lookup_hash(q):
"""Given a string query q of one hash, lookup its hash to the backend.
Args:
query, hash as a string (sha1, sha256, etc...)
Returns:
a string message (found, not found or a potential error explanation)
Raises:
OSError (no route to host), etc... Network issues in general
"""
hash = query.categorize_hash(q)
if hash != {}:
present = main.storage().content_exist(hash)
return 'Found!' if present else 'Not Found'
return """This is not a hash.
Hint: hexadecimal string with length either 20 (sha1) or 32 (sha256)."""
def _origin_seen(hash, data):
"""Given an origin, compute a message string with the right information.
Args:
origin: a dictionary with keys:
- origin: a dictionary with type and url keys
- occurrence: a dictionary with a validity range
Returns:
message as a string
"""
if data is None:
return 'Content with hash %s is unknown as of now.' % hash
origin_type = data['origin_type']
origin_url = data['origin_url']
revision = data['revision']
branch = data['branch']
path = data['path']
print("data:", data)
return """The content with hash %s has been seen on origin with type '%s'
at url '%s'. The revision was identified at '%s' on branch '%s'.
The file's path referenced was '%s'.""" % (hash,
origin_type,
origin_url,
revision,
branch,
path)
def lookup_hash_origin(hash):
"""Given a hash, return the origin of such content if any is found.
Args:
hash: key/value dictionary
Returns:
The origin for such hash if it's found.
Raises:
OSError (no route to host), etc... Network issues in general
"""
data = main.storage().content_find_occurrence(hash)
return _origin_seen(hash, data)
+
+
+def stat_counters():
+ """Return the stat counters for Software Heritage
+
+ Returns:
+ A dict mapping textual labels to integer values.
+ """
+ return main.storage().stat_counters()
diff --git a/swh/web/ui/tests/test_controller.py b/swh/web/ui/tests/test_controller.py
index b6ef2518..3a37e45b 100644
--- a/swh/web/ui/tests/test_controller.py
+++ b/swh/web/ui/tests/test_controller.py
@@ -1,54 +1,61 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import unittest
from nose.tools import istest
from swh.web.ui import controller
class ApiTestCase(unittest.TestCase):
@classmethod
def setUpClass(self):
conf = {'api_backend': 'https://somewhere.org:4321'}
controller.app.config['TESTING'] = True
controller.app.config.update({'conf': conf})
self.app = controller.app.test_client()
@istest
def info(self):
# when
rv = self.app.get('/info')
self.assertEquals(rv.status_code, 200)
self.assertEquals(rv.data, b'Dev SWH UI')
@istest
def main_redirects_to_info(self):
# when
rv = self.app.get('/', follow_redirects=False)
self.assertEquals(rv.status_code, 302) # check redirects to /info
# @istest
def search_1(self):
# when
rv = self.app.get('/search')
self.assertEquals(rv.status_code, 200) # check this api
self.assertRegexpMatches(rv.data, b'name=q value=>')
# @istest
def search_2(self):
# when
rv = self.app.get('/search?q=one-hash-to-look-for:another-one')
self.assertEquals(rv.status_code, 200) # check this api
self.assertRegexpMatches(
rv.data,
b'name=q value=one-hash-to-look-for:another-one')
+
+ # @istest
+ def api_1_stat_counters(self):
+ rv = self.app.get('/api/1/stat/counters')
+
+ self.assertEquals(rv.status_code, 200)
+ self.assertEquals(rv.mimetype, 'application/json')

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 10:45 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3254458

Event Timeline