-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨(client) add dynamic endpoints and CLI commands
We are now able to query /dynamique API endpoints using the client but also the CLI, yay!
- Loading branch information
Showing
15 changed files
with
1,160 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
"""QualiCharge API client CLI: api.""" | ||
|
||
from typing import Any | ||
|
||
import typer | ||
from anyio import run | ||
from rich import print | ||
|
||
from ..exceptions import APIRequestError | ||
from .codes import QCCExitCodes | ||
|
||
|
||
def async_run_api_query(*args) -> Any: | ||
"""An anyio.run wrapper to handle APIRequestError.""" | ||
try: | ||
return_value = run(*args) | ||
except APIRequestError as err: | ||
print("[red]An error occurred while querying the API! More details follow.") | ||
print(err.args[0]) | ||
raise typer.Exit(QCCExitCodes.API_EXCEPTION) from err | ||
return return_value |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
"""QualiCharge API client CLI: statuc.""" | ||
|
||
from typing import Annotated, Optional | ||
|
||
import click | ||
import typer | ||
from rich import print | ||
|
||
from ..client import QCC | ||
from .api import async_run_api_query | ||
from .utils import parse_input_json_lines, parse_json_parameter | ||
|
||
app = typer.Typer(name="session", no_args_is_help=True) | ||
|
||
|
||
@app.command() | ||
def create( | ||
ctx: typer.Context, | ||
session: Optional[str] = None, | ||
interactive: Annotated[ | ||
bool, typer.Option(help="Read session from standard input (JSON string)") | ||
] = True, | ||
): | ||
"""Create a charging point session. | ||
You can submit your session entry to create as a JSON string argument for | ||
the `--status` option. Without `--status` option (but with `--interactive`) | ||
the command will read and parse the standard input as a JSON string. | ||
Note that when using the `--interactive` option (active by default), the command | ||
expects your JSON string on a single row. | ||
""" | ||
client: QCC = ctx.obj | ||
data = parse_json_parameter("session", session, interactive) # type: ignore[arg-type] | ||
created = async_run_api_query(client.session.create, data) | ||
print("[green]Created session successfully.[/green]") | ||
print(created) | ||
|
||
|
||
@app.command() | ||
def bulk( | ||
ctx: typer.Context, | ||
chunk_size: int = 10, | ||
ignore_errors: bool = False, | ||
): | ||
"""Bulk create new sessions. | ||
Sessions will be read from the standard input (one JSON per line). | ||
""" | ||
client: QCC = ctx.obj | ||
|
||
n_created = async_run_api_query( | ||
client.session.bulk, | ||
parse_input_json_lines(click.get_text_stream("stdin"), ignore_errors), | ||
chunk_size, | ||
ignore_errors, | ||
) | ||
|
||
print(f"[green]Created {n_created} sessions successfully.[/green]") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
"""QualiCharge API client CLI: status.""" | ||
|
||
import json | ||
from datetime import datetime | ||
from typing import Annotated, List, Optional | ||
|
||
import click | ||
import typer | ||
from rich import print | ||
|
||
from ..client import QCC | ||
from .api import async_run_api_query | ||
from .utils import parse_input_json_lines, parse_json_parameter | ||
|
||
app = typer.Typer(name="status", no_args_is_help=True) | ||
|
||
|
||
@app.command() | ||
def list( | ||
ctx: typer.Context, | ||
from_: Annotated[Optional[datetime], typer.Option("--from")] = None, | ||
pdc: Optional[List[str]] = None, | ||
station: Optional[List[str]] = None, | ||
): | ||
"""List charging points last known status.""" | ||
client: QCC = ctx.obj | ||
|
||
async def statuses(): | ||
async for status in client.status.list(from_=from_, pdc=pdc, station=station): | ||
typer.echo(json.dumps(status)) | ||
|
||
async_run_api_query(statuses) | ||
|
||
|
||
@app.command() | ||
def create( | ||
ctx: typer.Context, | ||
status: Optional[str] = None, | ||
interactive: Annotated[ | ||
bool, typer.Option(help="Read status from standard input (JSON string)") | ||
] = True, | ||
): | ||
"""Create a charging point status. | ||
You can submit your status entry to create as a JSON string argument for | ||
the `--status` option. Without `--status` option (but with `--interactive`) | ||
the command will read and parse the standard input as a JSON string. | ||
Note that when using the `--interactive` option (active by default), the command | ||
expects your JSON string on a single row. | ||
""" | ||
client: QCC = ctx.obj | ||
data = parse_json_parameter("status", status, interactive) # type: ignore[arg-type] | ||
created = async_run_api_query(client.status.create, data) | ||
print("[green]Created status successfully.[/green]") | ||
print(created) | ||
|
||
|
||
@app.command() | ||
def read(ctx: typer.Context, id_pdc_itinerance: str): | ||
"""Get charging point status.""" | ||
client: QCC = ctx.obj | ||
|
||
read = async_run_api_query(client.status.read, id_pdc_itinerance) | ||
typer.echo(json.dumps(read)) | ||
|
||
|
||
@app.command() | ||
def history( | ||
ctx: typer.Context, | ||
id_pdc_itinerance: str, | ||
from_: Annotated[Optional[datetime], typer.Option("--from")] = None, | ||
): | ||
"""Get charging point history.""" | ||
client: QCC = ctx.obj | ||
|
||
async def statuses(): | ||
async for status in client.status.history(id_pdc_itinerance, from_=from_): | ||
typer.echo(json.dumps(status)) | ||
|
||
async_run_api_query(statuses) | ||
|
||
|
||
@app.command() | ||
def bulk( | ||
ctx: typer.Context, | ||
chunk_size: int = 10, | ||
ignore_errors: bool = False, | ||
): | ||
"""Bulk create new statuses. | ||
Statuses will be read from the standard input (one JSON per line). | ||
""" | ||
client: QCC = ctx.obj | ||
|
||
n_created = async_run_api_query( | ||
client.status.bulk, | ||
parse_input_json_lines(click.get_text_stream("stdin"), ignore_errors), | ||
chunk_size, | ||
ignore_errors, | ||
) | ||
|
||
print(f"[green]Created {n_created} statuses successfully.[/green]") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
"""QualiCharge API client CLI: utils.""" | ||
|
||
import json | ||
from typing import Generator, TextIO | ||
|
||
import click | ||
import typer | ||
from rich import print | ||
|
||
from .codes import QCCExitCodes | ||
|
||
|
||
def parse_json_parameter(name: str, parameter: str, interactive: bool) -> dict: | ||
"""Read and JSON parse parameter from option or stdin.""" | ||
# Get parameter value from stdin if empty | ||
if not parameter and interactive: | ||
parameter = click.get_text_stream("stdin").readline() | ||
|
||
if parameter is None: | ||
print( | ||
( | ||
f"[red]A {name} object is required " | ||
"either from stdin or as an option[/red]" | ||
) | ||
) | ||
raise typer.Exit(QCCExitCodes.PARAMETER_EXCEPTION) | ||
|
||
# Parse parameter as JSON | ||
try: | ||
data = json.loads(parameter) | ||
except json.JSONDecodeError as err: | ||
print("[red]Invalid JSON input string[/red]") | ||
raise typer.Exit(QCCExitCodes.PARAMETER_EXCEPTION) from err | ||
return data | ||
|
||
|
||
def parse_input_json_lines( | ||
lines: TextIO, ignore_errors: bool | ||
) -> Generator[dict, None, None]: | ||
"""Read and JSON parse stdin line by line.""" | ||
for line in lines: | ||
try: | ||
data = json.loads(line) | ||
except json.JSONDecodeError as err: | ||
if ignore_errors: | ||
print(f"[orange]Ignored invalid line:[/orange]\n{line}") | ||
continue | ||
print("[red]Invalid JSON input string[/red]") | ||
raise typer.Exit(QCCExitCodes.PARAMETER_EXCEPTION) from err | ||
yield data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.