Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9312184
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rDWAPPS Web applications
Event Timeline
Log In to Comment