diff --git a/swh/web/ui/backend.py b/swh/web/ui/backend.py --- a/swh/web/ui/backend.py +++ b/swh/web/ui/backend.py @@ -355,3 +355,15 @@ """ return main.storage().entity_get(uuid) + + +def vault_cook(obj_type, obj_id, email=None): + """Cook a vault bundle. + """ + return main.vault().cook(obj_type, obj_id, email=email) + + +def vault_fetch(obj_type, obj_id): + """Fetch a vault bundle. + """ + return main.vault().fetch(obj_type, obj_id) diff --git a/swh/web/ui/main.py b/swh/web/ui/main.py --- a/swh/web/ui/main.py +++ b/swh/web/ui/main.py @@ -17,6 +17,7 @@ from swh.web.ui.renderers import revision_id_from_url, highlight_source from swh.web.ui.renderers import SWHMultiResponse, urlize_header_links from swh.storage import get_storage +from swh.vault.api.client import RemoteVaultCache DEFAULT_CONFIG = { @@ -26,6 +27,7 @@ 'url': 'http://127.0.0.1:5002/', }, }), + 'vault': ('string', 'http://127.0.0.1:5005/'), 'log_dir': ('string', '/tmp/swh/log'), 'debug': ('bool', None), 'host': ('string', '127.0.0.1'), @@ -64,6 +66,7 @@ conf = config.read(config_file, DEFAULT_CONFIG) config.prepare_folders(conf, 'log_dir') conf['storage'] = get_storage(**conf['storage']) + conf['vault'] = RemoteVaultCache(conf['vault']) return conf @@ -95,6 +98,12 @@ """ return app.config['conf']['storage'] +def vault(): + """Return the current application's storage. + + """ + return app.config['conf']['vault'] + def prepare_limiter(): """Prepare Flask Limiter from configuration and App configuration""" if hasattr(app, 'limiter'): diff --git a/swh/web/ui/service.py b/swh/web/ui/service.py --- a/swh/web/ui/service.py +++ b/swh/web/ui/service.py @@ -767,3 +767,11 @@ raise NotFoundExc('Revision with criterion %s not found!' % revision) return (rev['id'], lookup_directory_with_revision(rev['id'], path, with_data)) + + +def vault_cook(obj_type, obj_id, email=None): + return backend.vault_cook(obj_type, obj_id, email=email) + + +def vault_fetch(obj_type, obj_id): + return backend.vault_fetch(obj_type, obj_id) diff --git a/swh/web/ui/views/api.py b/swh/web/ui/views/api.py --- a/swh/web/ui/views/api.py +++ b/swh/web/ui/views/api.py @@ -1103,5 +1103,119 @@ """ return _api_lookup( service.lookup_entity_by_uuid, uuid, - notfound_msg="Entity with uuid '%s' not found." % uuid, + notfound_msg="Entity with uuid '{}' not found.".format(uuid), enrich_fn=utils.enrich_entity) + + +@app.route('/api/1/vault/directory//', + methods=['GET', 'POST']) +@doc.route('/api/1/vault/directory', tags=['hidden']) +@doc.arg('dir_id', + default='', + argtype=doc.argtypes.sha1_git, + argdoc="The directory's sha1 identifier") +@doc.param('email', default=None, + argtype=doc.argtypes.int, + doc="e-mail to notify when the bundle is ready") +@doc.raises(exc=doc.excs.badinput, doc=_doc_exc_bad_id) +@doc.raises(exc=doc.excs.notfound, doc=_doc_exc_id_not_found) +@doc.returns(rettype=doc.rettypes.dict, + retdoc='dictionary mapping containing the status of the cooking') +def api_vault_cook_directory(dir_id): + """Requests an archive of the directoy identified by dir_id. + + To import the directory in the current directory, use:: + + $ tar xvf path/to/directory.tar.gz + """ + email = request.values.get('email', None) + + def _enrich_dir_cook(res): + res['fetch_url'] = url_for('api_vault_fetch_directory', dir_id=dir_id) + return res + + return _api_lookup( + service.vault_cook, 'directory', dir_id, email, + notfound_msg="Directory with ID '{}' not found.".format(dir_id), + enrich_fn=_enrich_dir_cook) + + +@app.route('/api/1/vault/directory//raw/') +@doc.route('/api/1/vault/directory/raw', tags=['hidden']) +@doc.arg('dir_id', + default='', + argtype=doc.argtypes.sha1_git, + argdoc="The directory's sha1 identifier") +@doc.raises(exc=doc.excs.badinput, doc=_doc_exc_bad_id) +@doc.raises(exc=doc.excs.notfound, doc=_doc_exc_id_not_found) +@doc.returns(rettype=doc.rettypes.octet_stream, + retdoc='the cooked directory tarball') +def api_vault_fetch_directory(dir_id): + """Fetch the archive of the directoy identified by dir_id.""" + res = _api_lookup( + service.vault_fetch, 'directory', dir_id, + notfound_msg="Directory with ID '{}' not found.".format(dir_id)) + filename = '{}.tar.gz'.format(dir_id) + + return app.response_class(res, + headers={'Content-disposition': 'attachment;' + 'filename=%s' % filename}, + mimetype='application/gzip') + + +@app.route('/api/1/vault/revision_gitfast//', + methods=['GET', 'POST']) +@doc.route('/api/1/vault/revision_gitfast', tags=['hidden']) +@doc.arg('rev_id', + default='', + argtype=doc.argtypes.sha1_git, + argdoc="The revision's sha1_git identifier") +@doc.param('email', default=None, + argtype=doc.argtypes.int, + doc="e-mail to notify when the bundle is ready") +@doc.raises(exc=doc.excs.badinput, doc=_doc_exc_bad_id) +@doc.raises(exc=doc.excs.notfound, doc=_doc_exc_id_not_found) +@doc.returns(rettype=doc.rettypes.dict, + retdoc='dictionary mapping containing the status of the cooking') +def api_vault_cook_revision_gitfast(rev_id): + """Requests an archive of the revision identified by rev_id. + + To import the revision in the current directory, use:: + + $ git init + $ zcat path/to/revision.gitfast.gz | git fast-import + """ + email = request.values.get('email', None) + + def _enrich_dir_cook(res): + res['fetch_url'] = url_for('api_vault_fetch_revision_gitfast', + rev_id=rev_id) + return res + + return _api_lookup( + service.vault_cook, 'revision_gitfast', rev_id, email, + notfound_msg="Revision with ID '{}' not found.".format(rev_id), + enrich_fn=_enrich_dir_cook) + + +@app.route('/api/1/vault/revision_gitfast//raw/') +@doc.route('/api/1/vault/revision_gitfast/raw', tags=['hidden']) +@doc.arg('rev_id', + default='', + argtype=doc.argtypes.sha1_git, + argdoc="The revision's sha1_git identifier") +@doc.raises(exc=doc.excs.badinput, doc=_doc_exc_bad_id) +@doc.raises(exc=doc.excs.notfound, doc=_doc_exc_id_not_found) +@doc.returns(rettype=doc.rettypes.octet_stream, + retdoc='the cooked revision git fast-export') +def api_vault_fetch_revision_gitfast(rev_id): + """Fetch the archive of the revision identified by rev_id.""" + res = _api_lookup( + service.vault_fetch, 'revision_gitfast', rev_id, + notfound_msg="Revision with ID '{}' not found.".format(rev_id)) + filename = '{}.gitfast.gz'.format(rev_id) + + return app.response_class(res, + headers={'Content-disposition': 'attachment;' + 'filename=%s' % filename}, + mimetype='application/gzip')