mirror of
https://github.com/simonw/dclient.git
synced 2026-09-10 02:24:22 +02:00
Moved default commands to dclient default ..., closes #30
This commit is contained in:
parent
7a8643f72f
commit
2f7f2a7378
6 changed files with 220 additions and 129 deletions
|
|
@ -32,8 +32,8 @@ datasette install dclient
|
|||
Add an alias for a Datasette instance:
|
||||
```bash
|
||||
dclient alias add latest https://latest.datasette.io
|
||||
dclient alias default latest
|
||||
dclient alias default-db latest fixtures
|
||||
dclient default instance latest
|
||||
dclient default database latest fixtures
|
||||
```
|
||||
Now run queries directly:
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -53,8 +53,11 @@ def _resolve_instance(instance, config_file):
|
|||
)
|
||||
# Try config default
|
||||
default = config.get("default_instance")
|
||||
if default and default in config.get("instances", {}):
|
||||
return config["instances"][default]["url"]
|
||||
if default:
|
||||
if default in config.get("instances", {}):
|
||||
return config["instances"][default]["url"]
|
||||
if default.startswith("http://") or default.startswith("https://"):
|
||||
return default.rstrip("/")
|
||||
# Try env var
|
||||
env_url = os.environ.get("DATASETTE_URL")
|
||||
if env_url:
|
||||
|
|
@ -62,7 +65,7 @@ def _resolve_instance(instance, config_file):
|
|||
raise click.ClickException(
|
||||
"No instance specified. Use -i <url-or-alias>, or configure a default:\n\n"
|
||||
" dclient alias add <name> <url>\n"
|
||||
" dclient alias default <name>\n\n"
|
||||
" dclient default instance <name-or-url>\n\n"
|
||||
"Or set the DATASETTE_URL environment variable."
|
||||
)
|
||||
|
||||
|
|
@ -75,8 +78,13 @@ def _resolve_database(database, instance_alias, config_file):
|
|||
if instance_alias:
|
||||
config = _load_config(config_file)
|
||||
instances = config.get("instances", {})
|
||||
if instance_alias in instances:
|
||||
default_db = instances[instance_alias].get("default_database")
|
||||
key = instance_alias
|
||||
if key not in instances and (
|
||||
key.startswith("http://") or key.startswith("https://")
|
||||
):
|
||||
key = _instance_alias_for_url(key, config_file)
|
||||
if key in instances:
|
||||
default_db = instances[key].get("default_database")
|
||||
if default_db:
|
||||
return default_db
|
||||
# Try env var
|
||||
|
|
@ -85,7 +93,7 @@ def _resolve_database(database, instance_alias, config_file):
|
|||
return env_db
|
||||
raise click.ClickException(
|
||||
"No database specified. Use -d <name>, or configure a default:\n\n"
|
||||
" dclient alias default-db <alias> <database>\n\n"
|
||||
" dclient default database <alias-or-url> <database>\n\n"
|
||||
"Or set the DATASETTE_DATABASE environment variable."
|
||||
)
|
||||
|
||||
|
|
@ -902,29 +910,56 @@ def alias_remove(name):
|
|||
raise click.ClickException("No such alias")
|
||||
|
||||
|
||||
@alias.command(name="default")
|
||||
@click.argument("name", required=False, default=None)
|
||||
def _resolve_instance_key(alias_or_url, config):
|
||||
instances = config.get("instances", {})
|
||||
if alias_or_url in instances:
|
||||
return alias_or_url
|
||||
if alias_or_url.startswith("http://") or alias_or_url.startswith("https://"):
|
||||
normalized = alias_or_url.rstrip("/")
|
||||
for name, inst in instances.items():
|
||||
if inst.get("url", "").rstrip("/") == normalized:
|
||||
return name
|
||||
raise click.ClickException(f"No such instance URL: {alias_or_url}")
|
||||
raise click.ClickException(f"No such alias: {alias_or_url}")
|
||||
|
||||
|
||||
# -- default command group --
|
||||
|
||||
|
||||
@cli.group()
|
||||
def default():
|
||||
"Manage default instance and database"
|
||||
|
||||
|
||||
@default.command(name="instance")
|
||||
@click.argument("alias_or_url", required=False, default=None)
|
||||
@click.option("--clear", is_flag=True, help="Clear default instance")
|
||||
def alias_default(name, clear):
|
||||
def default_instance(alias_or_url, clear):
|
||||
"""
|
||||
Set or show the default instance
|
||||
|
||||
Example usage:
|
||||
|
||||
\b
|
||||
dclient alias default prod
|
||||
dclient alias default
|
||||
dclient alias default --clear
|
||||
dclient default instance prod
|
||||
dclient default instance https://myapp.datasette.cloud
|
||||
dclient default instance
|
||||
dclient default instance --clear
|
||||
"""
|
||||
config_file = get_config_dir() / "config.json"
|
||||
config = _load_config(config_file)
|
||||
if clear:
|
||||
config["default_instance"] = None
|
||||
_save_config(config_file, config)
|
||||
elif name:
|
||||
if name not in config.get("instances", {}):
|
||||
raise click.ClickException(f"No such alias: {name}")
|
||||
config["default_instance"] = name
|
||||
elif alias_or_url:
|
||||
if alias_or_url.startswith("http://") or alias_or_url.startswith("https://"):
|
||||
try:
|
||||
key = _resolve_instance_key(alias_or_url, config)
|
||||
except click.ClickException:
|
||||
key = alias_or_url.rstrip("/")
|
||||
else:
|
||||
key = _resolve_instance_key(alias_or_url, config)
|
||||
config["default_instance"] = key
|
||||
_save_config(config_file, config)
|
||||
else:
|
||||
default = config.get("default_instance")
|
||||
|
|
@ -934,37 +969,37 @@ def alias_default(name, clear):
|
|||
click.echo("No default instance set")
|
||||
|
||||
|
||||
@alias.command(name="default-db")
|
||||
@click.argument("alias_name")
|
||||
@default.command(name="database")
|
||||
@click.argument("alias_or_url")
|
||||
@click.argument("db", required=False, default=None)
|
||||
@click.option("--clear", is_flag=True, help="Clear default database for this alias")
|
||||
def alias_default_db(alias_name, db, clear):
|
||||
@click.option("--clear", is_flag=True, help="Clear default database for this instance")
|
||||
def default_database(alias_or_url, db, clear):
|
||||
"""
|
||||
Set or show the default database for an alias
|
||||
Set or show the default database for an instance
|
||||
|
||||
Example usage:
|
||||
|
||||
\b
|
||||
dclient alias default-db prod main
|
||||
dclient alias default-db prod
|
||||
dclient alias default-db prod --clear
|
||||
dclient default database prod main
|
||||
dclient default database https://myapp.datasette.cloud main
|
||||
dclient default database prod
|
||||
dclient default database prod --clear
|
||||
"""
|
||||
config_file = get_config_dir() / "config.json"
|
||||
config = _load_config(config_file)
|
||||
if alias_name not in config.get("instances", {}):
|
||||
raise click.ClickException(f"No such alias: {alias_name}")
|
||||
instance_key = _resolve_instance_key(alias_or_url, config)
|
||||
if clear:
|
||||
config["instances"][alias_name]["default_database"] = None
|
||||
config["instances"][instance_key]["default_database"] = None
|
||||
_save_config(config_file, config)
|
||||
elif db:
|
||||
config["instances"][alias_name]["default_database"] = db
|
||||
config["instances"][instance_key]["default_database"] = db
|
||||
_save_config(config_file, config)
|
||||
else:
|
||||
default_db = config["instances"][alias_name].get("default_database")
|
||||
default_db = config["instances"][instance_key].get("default_database")
|
||||
if default_db:
|
||||
click.echo(default_db)
|
||||
else:
|
||||
click.echo(f"No default database set for {alias_name}")
|
||||
click.echo(f"No default database set for {instance_key}")
|
||||
|
||||
|
||||
# -- auth command group --
|
||||
|
|
|
|||
|
|
@ -13,28 +13,7 @@ Once registered, you can pass an alias to commands using the `-i` flag:
|
|||
|
||||
dclient query fixtures "select * from news limit 1" -i latest
|
||||
|
||||
## Default instance
|
||||
|
||||
Set a default instance so you don't need `-i` every time:
|
||||
|
||||
dclient alias default latest
|
||||
|
||||
Now commands will use `latest` automatically:
|
||||
|
||||
dclient databases
|
||||
dclient tables -d fixtures
|
||||
|
||||
## Default database
|
||||
|
||||
Set a default database for an alias:
|
||||
|
||||
dclient alias default-db latest fixtures
|
||||
|
||||
Now you can run bare SQL queries directly:
|
||||
|
||||
dclient "select * from facetable limit 5"
|
||||
|
||||
This uses the default instance and default database.
|
||||
See [Defaults](defaults.md) for default instance and default database settings.
|
||||
|
||||
## dclient alias --help
|
||||
<!-- [[[cog
|
||||
|
|
@ -57,11 +36,9 @@ Options:
|
|||
--help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
add Add an alias for a Datasette instance
|
||||
default Set or show the default instance
|
||||
default-db Set or show the default database for an alias
|
||||
list List aliases
|
||||
remove Remove an alias
|
||||
add Add an alias for a Datasette instance
|
||||
list List aliases
|
||||
remove Remove an alias
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
|
@ -137,59 +114,3 @@ Options:
|
|||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
||||
## dclient alias default --help
|
||||
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
result = runner.invoke(cli.cli, ["alias", "default", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient alias default [OPTIONS] [NAME]
|
||||
|
||||
Set or show the default instance
|
||||
|
||||
Example usage:
|
||||
|
||||
dclient alias default prod
|
||||
dclient alias default
|
||||
dclient alias default --clear
|
||||
|
||||
Options:
|
||||
--clear Clear default instance
|
||||
--help Show this message and exit.
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
||||
## dclient alias default-db --help
|
||||
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
result = runner.invoke(cli.cli, ["alias", "default-db", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient alias default-db [OPTIONS] ALIAS_NAME [DB]
|
||||
|
||||
Set or show the default database for an alias
|
||||
|
||||
Example usage:
|
||||
|
||||
dclient alias default-db prod main
|
||||
dclient alias default-db prod
|
||||
dclient alias default-db prod --clear
|
||||
|
||||
Options:
|
||||
--clear Clear default database for this alias
|
||||
--help Show this message and exit.
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
|
|
|||
106
docs/defaults.md
Normal file
106
docs/defaults.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Defaults
|
||||
|
||||
Set a default instance so you don't need `-i` every time:
|
||||
|
||||
dclient default instance latest
|
||||
|
||||
Now commands will use `latest` automatically:
|
||||
|
||||
dclient databases
|
||||
dclient tables -d fixtures
|
||||
|
||||
Set a default database for an alias or instance URL:
|
||||
|
||||
dclient default database latest fixtures
|
||||
dclient default database https://latest.datasette.io fixtures
|
||||
|
||||
Now you can run bare SQL queries directly:
|
||||
|
||||
dclient "select * from facetable limit 5"
|
||||
|
||||
This uses the default instance and default database.
|
||||
|
||||
## dclient default --help
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
from dclient import cli
|
||||
from click.testing import CliRunner
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli.cli, ["default", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient default [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
Manage default instance and database
|
||||
|
||||
Options:
|
||||
--help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
database Set or show the default database for an instance
|
||||
instance Set or show the default instance
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
||||
## dclient default instance --help
|
||||
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
result = runner.invoke(cli.cli, ["default", "instance", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient default instance [OPTIONS] [ALIAS_OR_URL]
|
||||
|
||||
Set or show the default instance
|
||||
|
||||
Example usage:
|
||||
|
||||
dclient default instance prod
|
||||
dclient default instance https://myapp.datasette.cloud
|
||||
dclient default instance
|
||||
dclient default instance --clear
|
||||
|
||||
Options:
|
||||
--clear Clear default instance
|
||||
--help Show this message and exit.
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
||||
## dclient default database --help
|
||||
|
||||
<!-- [[[cog
|
||||
import cog
|
||||
result = runner.invoke(cli.cli, ["default", "database", "--help"])
|
||||
help = result.output.replace("Usage: cli", "Usage: dclient")
|
||||
cog.out(
|
||||
"```\n{}\n```".format(help)
|
||||
)
|
||||
]]] -->
|
||||
```
|
||||
Usage: dclient default database [OPTIONS] ALIAS_OR_URL [DB]
|
||||
|
||||
Set or show the default database for an instance
|
||||
|
||||
Example usage:
|
||||
|
||||
dclient default database prod main
|
||||
dclient default database https://myapp.datasette.cloud main
|
||||
dclient default database prod
|
||||
dclient default database prod --clear
|
||||
|
||||
Options:
|
||||
--clear Clear default database for this instance
|
||||
--help Show this message and exit.
|
||||
|
||||
```
|
||||
<!-- [[[end]]] -->
|
||||
|
|
@ -35,6 +35,7 @@ maxdepth: 3
|
|||
---
|
||||
queries
|
||||
aliases
|
||||
defaults
|
||||
authentication
|
||||
inserting
|
||||
environment
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Tests for v2 alias command: default, default-db subcommands."""
|
||||
"""Tests for v2 defaults command: instance and database subcommands."""
|
||||
|
||||
from click.testing import CliRunner
|
||||
from dclient.cli import cli
|
||||
|
|
@ -15,16 +15,16 @@ def test_alias_default_workflow(mocker, tmpdir):
|
|||
assert result.exit_code == 0
|
||||
|
||||
# No default yet
|
||||
result = runner.invoke(cli, ["alias", "default"])
|
||||
result = runner.invoke(cli, ["default", "instance"])
|
||||
assert result.exit_code == 0
|
||||
assert "No default instance set" in result.output
|
||||
|
||||
# Set default
|
||||
result = runner.invoke(cli, ["alias", "default", "prod"])
|
||||
result = runner.invoke(cli, ["default", "instance", "prod"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Show default
|
||||
result = runner.invoke(cli, ["alias", "default"])
|
||||
result = runner.invoke(cli, ["default", "instance"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.strip() == "prod"
|
||||
|
||||
|
|
@ -34,17 +34,17 @@ def test_alias_default_workflow(mocker, tmpdir):
|
|||
assert "* prod" in result.output
|
||||
|
||||
# Clear default
|
||||
result = runner.invoke(cli, ["alias", "default", "--clear"])
|
||||
result = runner.invoke(cli, ["default", "instance", "--clear"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(cli, ["alias", "default"])
|
||||
result = runner.invoke(cli, ["default", "instance"])
|
||||
assert "No default instance set" in result.output
|
||||
|
||||
|
||||
def test_alias_default_unknown_alias(mocker, tmpdir):
|
||||
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["alias", "default", "nonexistent"])
|
||||
result = runner.invoke(cli, ["default", "instance", "nonexistent"])
|
||||
assert result.exit_code == 1
|
||||
assert "No such alias" in result.output
|
||||
|
||||
|
|
@ -58,16 +58,16 @@ def test_alias_default_db_workflow(mocker, tmpdir):
|
|||
assert result.exit_code == 0
|
||||
|
||||
# No default database yet
|
||||
result = runner.invoke(cli, ["alias", "default-db", "prod"])
|
||||
result = runner.invoke(cli, ["default", "database", "prod"])
|
||||
assert result.exit_code == 0
|
||||
assert "No default database set" in result.output
|
||||
|
||||
# Set default database
|
||||
result = runner.invoke(cli, ["alias", "default-db", "prod", "main"])
|
||||
result = runner.invoke(cli, ["default", "database", "prod", "main"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Show default database
|
||||
result = runner.invoke(cli, ["alias", "default-db", "prod"])
|
||||
result = runner.invoke(cli, ["default", "database", "prod"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.strip() == "main"
|
||||
|
||||
|
|
@ -76,17 +76,17 @@ def test_alias_default_db_workflow(mocker, tmpdir):
|
|||
assert "(db: main)" in result.output
|
||||
|
||||
# Clear default database
|
||||
result = runner.invoke(cli, ["alias", "default-db", "prod", "--clear"])
|
||||
result = runner.invoke(cli, ["default", "database", "prod", "--clear"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(cli, ["alias", "default-db", "prod"])
|
||||
result = runner.invoke(cli, ["default", "database", "prod"])
|
||||
assert "No default database set" in result.output
|
||||
|
||||
|
||||
def test_alias_default_db_unknown_alias(mocker, tmpdir):
|
||||
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["alias", "default-db", "nonexistent", "main"])
|
||||
result = runner.invoke(cli, ["default", "database", "nonexistent", "main"])
|
||||
assert result.exit_code == 1
|
||||
assert "No such alias" in result.output
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ def test_alias_remove_clears_default(mocker, tmpdir):
|
|||
runner = CliRunner()
|
||||
|
||||
runner.invoke(cli, ["alias", "add", "prod", "https://prod.example.com"])
|
||||
runner.invoke(cli, ["alias", "default", "prod"])
|
||||
runner.invoke(cli, ["default", "instance", "prod"])
|
||||
|
||||
# Verify it's set
|
||||
config = json.loads((pathlib.Path(tmpdir) / "config.json").read_text())
|
||||
|
|
@ -109,3 +109,31 @@ def test_alias_remove_clears_default(mocker, tmpdir):
|
|||
config = json.loads((pathlib.Path(tmpdir) / "config.json").read_text())
|
||||
assert config["default_instance"] is None
|
||||
assert "prod" not in config["instances"]
|
||||
|
||||
|
||||
def test_default_instance_accepts_url(mocker, tmpdir):
|
||||
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
|
||||
runner = CliRunner()
|
||||
|
||||
runner.invoke(cli, ["alias", "add", "prod", "https://prod.example.com"])
|
||||
result = runner.invoke(cli, ["default", "instance", "https://prod.example.com"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(cli, ["default", "instance"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.strip() == "prod"
|
||||
|
||||
|
||||
def test_default_database_accepts_url(mocker, tmpdir):
|
||||
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
|
||||
runner = CliRunner()
|
||||
|
||||
runner.invoke(cli, ["alias", "add", "prod", "https://prod.example.com"])
|
||||
result = runner.invoke(
|
||||
cli, ["default", "database", "https://prod.example.com", "main"]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(cli, ["default", "database", "prod"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.strip() == "main"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue