Commit e565918a authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Added base32 encoded handle based PID resolution.

parent 871dd6c1
Pipeline #65318 passed with stages
in 15 minutes and 26 seconds
......@@ -428,12 +428,13 @@ export default class App extends React.Component {
}
},
'entry_pid': {
path: '/entry/pid/:pid',
path: '/entry/pid/:pid/:handle?',
key: (props) => `entry/pid/${props.match.params.pid}`,
render: props => {
const { match, ...rest } = props
if (match && match.params.pid) {
return (<ResolvePID {...rest} pid={match.params.pid} />)
const {pid, handle} = match.params
return (<ResolvePID {...rest} pid={handle ? pid + '/' + handle : pid} />)
} else {
return ''
}
......
......@@ -720,13 +720,27 @@ class RepoQuantitiesResource(Resource):
return search_request.execute(), 200
@ns.route('/pid/<int:pid>')
@ns.route('/pid/<path:pid>')
class RepoPidResource(Resource):
@api.doc('resolve_pid')
@api.response(404, 'Entry with PID does not exist')
@api.marshal_with(repo_calc_id_model, skip_none=True, code=200, description='Entry resolved')
@authenticate()
def get(self, pid: int):
def get(self, pid: str):
if '/' in pid:
prefix, pid = pid.split('/')
if prefix != '21.11132':
abort(400, 'Wrong PID format')
try:
pid_int = utils.decode_handle_id(pid)
except ValueError:
abort(400, 'Wrong PID format')
else:
try:
pid_int = int(pid)
except ValueError:
abort(400, 'Wrong PID format')
search_request = search.SearchRequest()
if g.user is not None:
......@@ -734,16 +748,16 @@ class RepoPidResource(Resource):
else:
search_request.owner('all')
search_request.search_parameter('pid', pid)
search_request.search_parameter('pid', pid_int)
results = list(search_request.execute_scan())
total = len(results)
if total == 0:
abort(404, 'Entry with PID %d does not exist' % pid)
abort(404, 'Entry with PID %s does not exist' % pid)
if total > 1:
utils.get_logger(__name__).error('Two entries for the same pid', pid=pid)
utils.get_logger(__name__).error('Two entries for the same pid', pid=pid_int)
result = results[0]
return dict(
......
......@@ -56,6 +56,22 @@ default_hash_len = 28
""" Length of hashes and hash-based ids (e.g. calc, upload) in nomad. """
def decode_handle_id(handle_str: str):
result = 0
for c in handle_str:
ordinal = ord(c.lower())
if 48 <= ordinal <= 57:
number = ordinal - 48
elif 97 <= ordinal <= 118:
number = ordinal - 87
else:
raise ValueError()
result = result * 32 + number
return result
def hash(*args, length: int = default_hash_len) -> str:
""" Creates a websave hash of the given length based on the repr of the given arguments. """
hash = hashlib.sha512()
......
......@@ -959,19 +959,26 @@ class TestRepo():
assert rv.status_code == 200
# TODO actual assertions
@pytest.mark.parametrize('pid, with_login, success', [
(2, True, True), (2, False, True),
(3, True, True), (3, False, False),
(4, True, True), (4, False, False)])
@pytest.mark.parametrize('pid_or_handle, with_login, success', [
('2', True, True), ('2', False, True),
('3', True, True), ('3', False, False),
('4', True, True), ('4', False, False),
('21.11132/2', True, True)])
def test_resolve_pid(
self, api, example_elastic_calcs, other_test_user_auth, pid, with_login,
self, api, example_elastic_calcs, other_test_user_auth, pid_or_handle, with_login,
success, no_warn):
rv = api.get(
'/repo/pid/%d' % pid,
'/repo/pid/%s' % pid_or_handle,
headers=other_test_user_auth if with_login else {})
assert rv.status_code == 200 if success else 404
try:
pid = str(int(pid_or_handle))
except ValueError:
pid = str(utils.decode_handle_id(pid_or_handle.split('/')[1]))
if success:
assert json.loads(rv.data)['calc_id'] == '%d' % pid
assert json.loads(rv.data)['calc_id'] == '%s' % pid
assert json.loads(rv.data)['upload_id'] == 'example_upload_id'
@pytest.mark.timeout(config.tests.default_timeout)
......
......@@ -250,6 +250,13 @@ class KeycloakMock:
_keycloak = infrastructure.keycloak
# use a session fixture in addition to the function fixture, to ensure mocked keycloak
# before other class, module, etc. scoped function are run
@pytest.fixture(scope='session', autouse=True)
def mocked_keycloak_session(monkeysession):
monkeysession.setattr('nomad.infrastructure.keycloak', KeycloakMock())
@pytest.fixture(scope='function', autouse=True)
def mocked_keycloak(monkeypatch):
monkeypatch.setattr('nomad.infrastructure.keycloak', KeycloakMock())
......
......@@ -17,6 +17,15 @@ import json
from nomad import utils
from tests import utils as test_utils
def test_decode_handle_id():
assert utils.decode_handle_id('a1') == 321
assert utils.decode_handle_id('6i370') == 6884576
with test_utils.assert_exception(ValueError):
utils.decode_handle_id('zz')
def test_timer(caplog):
with utils.timer(utils.get_logger('test_logger'), 'test measure'):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment