Implement the full spec for the old Docker V1 registry search API
This API is still (apparently) being used by the Docker CLI for `docker search` (why?!) and we therefore have customers expecting this to work the same way as the DockerHub.
This commit is contained in:
parent
6384b47849
commit
a0bc0e9488
2 changed files with 59 additions and 14 deletions
|
@ -289,27 +289,28 @@ def put_repository_auth(namespace_name, repo_name):
|
|||
def get_search():
|
||||
query = request.args.get('q')
|
||||
|
||||
try:
|
||||
limit = min(100, max(1, int(request.args.get('n', 25))))
|
||||
except ValueError:
|
||||
limit = 25
|
||||
|
||||
try:
|
||||
page = max(0, int(request.args.get('page', 1)))
|
||||
except ValueError:
|
||||
page = 1
|
||||
|
||||
username = None
|
||||
user = get_authenticated_user()
|
||||
if user is not None:
|
||||
username = user.username
|
||||
|
||||
results = []
|
||||
if query:
|
||||
_conduct_repo_search(username, query, results)
|
||||
|
||||
data = {
|
||||
"query": query,
|
||||
"num_results": len(results),
|
||||
"results" : results
|
||||
}
|
||||
|
||||
data = _conduct_repo_search(username, query, limit, page)
|
||||
resp = make_response(json.dumps(data), 200)
|
||||
resp.mimetype = 'application/json'
|
||||
return resp
|
||||
|
||||
|
||||
def _conduct_repo_search(username, query, results):
|
||||
def _conduct_repo_search(username, query, limit=25, page=1):
|
||||
""" Finds matching repositories. """
|
||||
def can_read(repo):
|
||||
if repo.is_public:
|
||||
|
@ -317,13 +318,32 @@ def _conduct_repo_search(username, query, results):
|
|||
|
||||
return ReadRepositoryPermission(repo.namespace_user.username, repo.name).can()
|
||||
|
||||
only_public = username is None
|
||||
matching_repos = model.get_sorted_matching_repositories(query, only_public, can_read, limit=5)
|
||||
# Note: We put a max 5 page limit here. The Docker CLI doesn't seem to use the
|
||||
# pagination and most customers hitting the API should be using V2 catalog, so this
|
||||
# is a safety net for our slow search below, since we have to use the slow approach
|
||||
# of finding *all* the results, and then slicing in-memory, because this old API requires
|
||||
# the *full* page count in the returned results.
|
||||
_MAX_PAGE_COUNT = 5
|
||||
page = min(page, _MAX_PAGE_COUNT)
|
||||
|
||||
for repo in matching_repos:
|
||||
only_public = username is None
|
||||
matching_repos = model.get_sorted_matching_repositories(query, only_public, can_read,
|
||||
limit=limit*_MAX_PAGE_COUNT)
|
||||
results = []
|
||||
for repo in matching_repos[(page - 1) * _MAX_PAGE_COUNT:limit]:
|
||||
results.append({
|
||||
'name': repo.namespace_name + '/' + repo.name,
|
||||
'description': repo.description,
|
||||
'is_public': repo.is_public,
|
||||
'href': '/repository/' + repo.namespace_name + '/' + repo.name
|
||||
})
|
||||
|
||||
# Defined: https://docs.docker.com/v1.6/reference/api/registry_api/
|
||||
return {
|
||||
'query': query,
|
||||
'num_results': len(results),
|
||||
'num_pages': (len(matching_repos) / limit) + 1,
|
||||
'page': page,
|
||||
'page_size': limit,
|
||||
'results': results,
|
||||
}
|
||||
|
|
|
@ -1289,6 +1289,31 @@ class V1RegistryTests(V1RegistryPullMixin, V1RegistryPushMixin, RegistryTestsMix
|
|||
self.assertEquals(1, len(data['results']))
|
||||
|
||||
|
||||
def test_search_pagination(self):
|
||||
# Check for the first page.
|
||||
resp = self.conduct('GET', '/v1/search', params=dict(q='s', n='1'))
|
||||
data = resp.json()
|
||||
self.assertEquals('s', data['query'])
|
||||
|
||||
self.assertEquals(1, data['num_results'])
|
||||
self.assertEquals(1, len(data['results']))
|
||||
|
||||
self.assertEquals(1, data['page'])
|
||||
self.assertTrue(data['num_pages'] > 1)
|
||||
|
||||
# Check for the followup pages.
|
||||
for page_index in range(1, data['num_pages']):
|
||||
resp = self.conduct('GET', '/v1/search', params=dict(q='s', n='1', page=page_index))
|
||||
data = resp.json()
|
||||
self.assertEquals('s', data['query'])
|
||||
|
||||
self.assertEquals(1, data['num_results'])
|
||||
self.assertEquals(1, len(data['results']))
|
||||
|
||||
self.assertEquals(1, data['page'])
|
||||
self.assertTrue(data['num_pages'] > 1)
|
||||
|
||||
|
||||
def test_users(self):
|
||||
# Not logged in, should 404.
|
||||
self.conduct('GET', '/v1/users', expected_code=404)
|
||||
|
|
Reference in a new issue