mirror of
https://github.com/simonw/dclient.git
synced 2026-07-23 17:34:32 +02:00
Initial dclient query command, refs #1
This commit is contained in:
parent
56c4672f37
commit
1ecb6a02e8
5 changed files with 94 additions and 27 deletions
26
README.md
26
README.md
|
|
@ -23,6 +23,32 @@ You can also use:
|
|||
|
||||
python -m dclient --help
|
||||
|
||||
### dclient query
|
||||
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
from dclient import cli
|
||||
from click.testing import CliRunner
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli.cli, ["query", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient query [OPTIONS] URL SQL
|
||||
|
||||
Run a SQL query against a Datasette database URL
|
||||
|
||||
Returns a JSON array of objects
|
||||
|
||||
Options:
|
||||
--help Show this message and exit.
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
||||
## Development
|
||||
|
||||
To contribute to this tool, first checkout the code. Then create a new virtual environment:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import click
|
||||
import httpx
|
||||
import json
|
||||
|
||||
|
||||
@click.group()
|
||||
|
|
@ -7,15 +9,25 @@ def cli():
|
|||
"A client CLI utility for Datasette instances"
|
||||
|
||||
|
||||
@cli.command(name="command")
|
||||
@click.argument(
|
||||
"example"
|
||||
)
|
||||
@click.option(
|
||||
"-o",
|
||||
"--option",
|
||||
help="An example option",
|
||||
)
|
||||
def first_command(example, option):
|
||||
"Command description goes here"
|
||||
click.echo("Here is some output")
|
||||
@cli.command()
|
||||
@click.argument("url")
|
||||
@click.argument("sql")
|
||||
def query(url, sql):
|
||||
"""
|
||||
Run a SQL query against a Datasette database URL
|
||||
|
||||
Returns a JSON array of objects
|
||||
"""
|
||||
if not url.endswith(".json"):
|
||||
url += ".json"
|
||||
response = httpx.get(url, params={"sql": sql, "_shape": "objects"})
|
||||
if response.status_code != 200 or not response.json()["ok"]:
|
||||
data = response.json()
|
||||
bits = []
|
||||
if data.get("title"):
|
||||
bits.append(data["title"])
|
||||
if data.get("error"):
|
||||
bits.append(data["error"])
|
||||
raise click.ClickException(": ".join(bits))
|
||||
else:
|
||||
click.echo(json.dumps(response.json()["rows"], indent=2))
|
||||
|
|
|
|||
8
setup.py
8
setup.py
|
|
@ -1,7 +1,7 @@
|
|||
from setuptools import setup
|
||||
import os
|
||||
|
||||
VERSION = "0.1"
|
||||
VERSION = "0.1a0"
|
||||
|
||||
|
||||
def get_long_description():
|
||||
|
|
@ -31,9 +31,7 @@ setup(
|
|||
[console_scripts]
|
||||
dclient=dclient.cli:cli
|
||||
""",
|
||||
install_requires=["click"],
|
||||
extras_require={
|
||||
"test": ["pytest"]
|
||||
},
|
||||
install_requires=["click", "httpx"],
|
||||
extras_require={"test": ["pytest", "pytest-httpx", "cogapp"]},
|
||||
python_requires=">=3.7",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
from click.testing import CliRunner
|
||||
from dclient.cli import cli
|
||||
|
||||
|
||||
def test_version():
|
||||
runner = CliRunner()
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.startswith("cli, version ")
|
||||
41
tests/test_query.py
Normal file
41
tests/test_query.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from click.testing import CliRunner
|
||||
from dclient.cli import cli
|
||||
import json
|
||||
|
||||
|
||||
def test_query_error(httpx_mock):
|
||||
httpx_mock.add_response(
|
||||
json={
|
||||
"ok": False,
|
||||
"error": "Statement must be a SELECT",
|
||||
"status": 400,
|
||||
"title": "Invalid SQL",
|
||||
},
|
||||
status_code=400,
|
||||
)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["query", "https://example.com", "hello"])
|
||||
assert result.exit_code == 1
|
||||
assert result.output == "Error: Invalid SQL: Statement must be a SELECT\n"
|
||||
|
||||
|
||||
def test_query(httpx_mock):
|
||||
httpx_mock.add_response(
|
||||
json={
|
||||
"ok": True,
|
||||
"database": "fixtures",
|
||||
"query_name": None,
|
||||
"rows": [{"5 * 2": 10}],
|
||||
"truncated": False,
|
||||
"columns": ["5 * 2"],
|
||||
"query": {"sql": "select 5 * 2", "params": {}},
|
||||
"error": None,
|
||||
"private": False,
|
||||
"allow_execute_sql": True,
|
||||
},
|
||||
status_code=200,
|
||||
)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["query", "https://example.com", "hello"])
|
||||
assert result.exit_code == 0
|
||||
assert json.loads(result.output) == [{"5 * 2": 10}]
|
||||
Loading…
Add table
Add a link
Reference in a new issue