In composer-cli, request all results

Add a limit argument to all potentially paginated results, equal to
whatever the composer backend is the total number of results. This still
has the potential to provide truncated data if the number of results
increases between the two HTTP requests.

Resolves: #404
This commit is contained in:
David Shea 2018-08-10 16:02:51 -04:00 committed by Brian C. Lane
parent c52ba4236a
commit ee98d87cea
4 changed files with 53 additions and 4 deletions

View File

@ -72,7 +72,7 @@ def blueprints_list(socket_path, api_version, args, show_json=False):
blueprints list blueprints list
""" """
api_route = client.api_url(api_version, "/blueprints/list") api_route = client.api_url(api_version, "/blueprints/list")
result = client.get_url_json(socket_path, api_route) result = client.get_url_json_unlimited(socket_path, api_route)
(rc, exit_now) = handle_api_result(result, show_json) (rc, exit_now) = handle_api_result(result, show_json)
if exit_now: if exit_now:
return rc return rc
@ -119,7 +119,7 @@ def blueprints_changes(socket_path, api_version, args, show_json=False):
blueprints changes <blueprint,...> Display the changes for each blueprint. blueprints changes <blueprint,...> Display the changes for each blueprint.
""" """
api_route = client.api_url(api_version, "/blueprints/changes/%s" % (",".join(argify(args)))) api_route = client.api_url(api_version, "/blueprints/changes/%s" % (",".join(argify(args))))
result = client.get_url_json(socket_path, api_route) result = client.get_url_json_unlimited(socket_path, api_route)
(rc, exit_now) = handle_api_result(result, show_json) (rc, exit_now) = handle_api_result(result, show_json)
if exit_now: if exit_now:
return rc return rc

View File

@ -37,7 +37,7 @@ def modules_cmd(opts):
return 1 return 1
api_route = client.api_url(opts.api_version, "/modules/list") api_route = client.api_url(opts.api_version, "/modules/list")
result = client.get_url_json(opts.socket, api_route) result = client.get_url_json_unlimited(opts.socket, api_route)
(rc, exit_now) = handle_api_result(result, opts.json) (rc, exit_now) = handle_api_result(result, opts.json)
if exit_now: if exit_now:
return rc return rc

View File

@ -59,7 +59,7 @@ def projects_list(socket_path, api_version, args, show_json=False):
projects list projects list
""" """
api_route = client.api_url(api_version, "/projects/list") api_route = client.api_url(api_version, "/projects/list")
result = client.get_url_json(socket_path, api_route) result = client.get_url_json_unlimited(socket_path, api_route)
(rc, exit_now) = handle_api_result(result, show_json) (rc, exit_now) = handle_api_result(result, show_json)
if exit_now: if exit_now:
return rc return rc

View File

@ -20,6 +20,7 @@ log = logging.getLogger("composer-cli")
import os import os
import sys import sys
import json import json
from urlparse import urlparse, urlunparse
from composer.unix_socket import UnixHTTPConnectionPool from composer.unix_socket import UnixHTTPConnectionPool
@ -35,6 +36,29 @@ def api_url(api_version, url):
""" """
return os.path.normpath("/api/v%s/%s" % (api_version, url)) return os.path.normpath("/api/v%s/%s" % (api_version, url))
def append_query(url, query):
"""Add a query argument to a URL
The query should be of the form "param1=what&param2=ever", i.e., no
leading '?'. The new query data will be appended to any existing
query string.
:param url: The original URL
:type url: str
:param query: The query to append
:type query: str
:returns: The new URL with the query argument included
:rtype: str
"""
url_parts = urlparse(url)
if url_parts.query:
new_query = url_parts.query + "&" + query
else:
new_query = query
return urlunparse([url_parts[0], url_parts[1], url_parts[2],
url_parts[3], new_query, url_parts[5]])
def get_url_raw(socket_path, url): def get_url_raw(socket_path, url):
"""Return the raw results of a GET request """Return the raw results of a GET request
@ -69,6 +93,31 @@ def get_url_json(socket_path, url):
r = http.request("GET", url) r = http.request("GET", url)
return json.loads(r.data.decode('utf-8')) return json.loads(r.data.decode('utf-8'))
def get_url_json_unlimited(socket_path, url):
"""Return the JSON results of a GET request
For URLs that use offset/limit arguments, this command will
fetch all results for the given request.
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param url: URL to request
:type url: str
:returns: The json response from the server
:rtype: dict
"""
http = UnixHTTPConnectionPool(socket_path)
# Start with limit=0 to just get the number of objects
total_url = append_query(url, "limit=0")
r_total = http.request("GET", total_url)
json_total = json.loads(r_total.data.decode('utf-8'))
# Add the "total" returned by limit=0 as the new limit
unlimited_url = append_query(url, "limit=%d" % json_total["total"])
r_unlimited = http.request("GET", unlimited_url)
return json.loads(r_unlimited.data.decode('utf-8'))
def delete_url_json(socket_path, url): def delete_url_json(socket_path, url):
"""Send a DELETE request to the url and return JSON response """Send a DELETE request to the url and return JSON response