phlo-postgres 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/PKG-INFO +1 -1
  2. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/README.md +2 -2
  3. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/pyproject.toml +1 -1
  4. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/__init__.py +1 -1
  5. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/cli.py +32 -8
  6. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/service.yaml +2 -2
  7. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/PKG-INFO +1 -1
  8. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/tests/test_postgres_cli.py +32 -6
  9. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/setup.cfg +0 -0
  10. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/authorization.py +0 -0
  11. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/cli_plugin.py +0 -0
  12. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/exporter_service.yaml +0 -0
  13. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/plugin.py +0 -0
  14. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/publish_target.py +0 -0
  15. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/resource.py +0 -0
  16. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/settings.py +0 -0
  17. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres/volume_setup.yaml +0 -0
  18. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/SOURCES.txt +0 -0
  19. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/dependency_links.txt +0 -0
  20. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/entry_points.txt +0 -0
  21. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/requires.txt +0 -0
  22. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/src/phlo_postgres.egg-info/top_level.txt +0 -0
  23. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/tests/test_authorization.py +0 -0
  24. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/tests/test_integration_postgres.py +0 -0
  25. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/tests/test_postgres_plugin.py +0 -0
  26. {phlo_postgres-0.3.0 → phlo_postgres-0.3.2}/tests/test_resource.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phlo-postgres
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Postgres service plugin for Phlo
5
5
  Author-email: Phlo Team <team@phlo.dev>
6
6
  License: MIT
@@ -18,7 +18,7 @@ phlo plugin install postgres
18
18
 
19
19
  | Variable | Default | Description |
20
20
  | ------------------------ | -------- | ---------------------- |
21
- | `POSTGRES_PORT` | `5432` | PostgreSQL port |
21
+ | `POSTGRES_PORT` | `10000` | PostgreSQL host port |
22
22
  | `POSTGRES_USER` | `phlo` | Database username |
23
23
  | `POSTGRES_PASSWORD` | `phlo` | Database password |
24
24
  | `POSTGRES_DB` | `phlo` | Database name |
@@ -57,7 +57,7 @@ phlo services start --service postgres,postgres-exporter
57
57
 
58
58
  ## Endpoints
59
59
 
60
- - **PostgreSQL**: `localhost:5432`
60
+ - **PostgreSQL**: `localhost:10000`
61
61
  - **Exporter Metrics**: `http://localhost:9187/metrics`
62
62
 
63
63
  ## Entry Points
@@ -14,7 +14,7 @@ dependencies = [
14
14
  description = "Postgres service plugin for Phlo"
15
15
  name = "phlo-postgres"
16
16
  requires-python = ">=3.11"
17
- version = "0.3.0"
17
+ version = "0.3.2"
18
18
 
19
19
  [[project.authors]]
20
20
  email = "team@phlo.dev"
@@ -27,4 +27,4 @@ __all__ = [
27
27
  "PostgresSettings",
28
28
  "get_settings",
29
29
  ]
30
- __version__ = "0.3.0"
30
+ __version__ = "0.3.2"
@@ -23,13 +23,22 @@ from subprocess import TimeoutExpired
23
23
 
24
24
  import click
25
25
 
26
+ from phlo.cli.authorization_wrappers import enforce_surface_mutation_authorization
26
27
  from phlo.cli.commands.services.utils import (
27
- ensure_phlo_dir,
28
+ ensure_compose_project,
28
29
  require_container_backend as _require_selected_container_backend,
29
30
  )
30
31
  from phlo.cli.infrastructure.command import CommandError, run_command
31
32
  from phlo.cli.infrastructure.compose import compose_base_cmd
32
33
  from phlo.cli.infrastructure.utils import get_project_name
34
+ from phlo.cli.output import (
35
+ command_failed_error,
36
+ empty_file_error,
37
+ exclusive_options_error,
38
+ file_read_error,
39
+ )
40
+ from phlo.cli.output import missing_query_error
41
+ from phlo_postgres.authorization import get_postgres_cli_adapter
33
42
  from phlo_postgres.settings import get_settings
34
43
 
35
44
 
@@ -56,18 +65,18 @@ def _read_sql(*, query: str | None, file: Path | None) -> str:
56
65
 
57
66
  """
58
67
  if query and file:
59
- raise click.ClickException("Use either an inline query or --file, not both.")
68
+ raise exclusive_options_error("an inline query", "--file")
60
69
  if file is not None:
61
70
  try:
62
71
  sql = file.read_text(encoding="utf-8")
63
72
  except OSError as exc:
64
- raise click.ClickException(f"Failed to read SQL file: {file}") from exc
73
+ raise file_read_error(file) from exc
65
74
  if sql.strip():
66
75
  return sql
67
- raise click.ClickException(f"SQL file is empty: {file}")
76
+ raise empty_file_error(file)
68
77
  if query and query.strip():
69
78
  return query
70
- raise click.ClickException("Provide a SQL query argument or --file.")
79
+ raise missing_query_error(command_hint='phlo postgres query "SELECT 1"')
71
80
 
72
81
 
73
82
  def _require_container_backend() -> None:
@@ -93,7 +102,7 @@ def _postgres_exec_base(*, tty: bool) -> list[str]:
93
102
  >>> # Returns: ['docker', 'compose', '-p', 'phlo', '-f', '...', 'exec', '-t', 'postgres']
94
103
 
95
104
  """
96
- phlo_dir = ensure_phlo_dir()
105
+ phlo_dir = ensure_compose_project()
97
106
  project_name = get_project_name()
98
107
  cmd = compose_base_cmd(phlo_dir=phlo_dir, project_name=project_name)
99
108
  cmd.append("exec")
@@ -183,6 +192,7 @@ def postgres_group(ctx: click.Context, postgres_args: tuple[str, ...]) -> None:
183
192
  return
184
193
 
185
194
  _require_container_backend()
195
+ enforce_surface_mutation_authorization("postgres", get_postgres_cli_adapter)
186
196
  user, database = _postgres_identity(user=None, database=None)
187
197
  cmd = _postgres_exec_base(tty=True)
188
198
  cmd.extend(["psql", "-U", user, "-d", database])
@@ -230,6 +240,7 @@ def postgres_query(
230
240
 
231
241
  """
232
242
  _require_container_backend()
243
+ enforce_surface_mutation_authorization("postgres.query", get_postgres_cli_adapter)
233
244
  sql = _read_sql(query=query, file=query_file)
234
245
  resolved_user, resolved_db = _postgres_identity(user=user, database=database)
235
246
  cmd = _postgres_exec_base(tty=False)
@@ -244,7 +255,12 @@ def postgres_query(
244
255
  )
245
256
  except CommandError as exc:
246
257
  stderr = exc.stderr.strip()
247
- raise click.ClickException(stderr or str(exc)) from exc
258
+ raise command_failed_error(
259
+ "psql",
260
+ exit_code=exc.returncode,
261
+ details=[stderr] if stderr else ["PostgreSQL did not complete the query."],
262
+ run="phlo services status postgres",
263
+ ) from exc
248
264
  except TimeoutExpired as exc:
249
265
  raise click.ClickException(f"Query timed out after {timeout_seconds} seconds.") from exc
250
266
 
@@ -289,6 +305,7 @@ def postgres_dump(
289
305
 
290
306
  """
291
307
  _require_container_backend()
308
+ enforce_surface_mutation_authorization("postgres.dump", get_postgres_cli_adapter)
292
309
  resolved_user, resolved_db = _postgres_identity(user=user, database=database)
293
310
  cmd = _postgres_exec_base(tty=False)
294
311
  cmd.extend(["pg_dump", "-U", resolved_user, resolved_db])
@@ -302,7 +319,12 @@ def postgres_dump(
302
319
  )
303
320
  except CommandError as exc:
304
321
  stderr = exc.stderr.strip()
305
- raise click.ClickException(stderr or str(exc)) from exc
322
+ raise command_failed_error(
323
+ "pg_dump",
324
+ exit_code=exc.returncode,
325
+ details=[stderr] if stderr else ["PostgreSQL did not create the dump."],
326
+ run="phlo services status postgres",
327
+ ) from exc
306
328
  except TimeoutExpired as exc:
307
329
  raise click.ClickException(f"Dump timed out after {timeout_seconds} seconds.") from exc
308
330
 
@@ -359,6 +381,7 @@ def postgres_restore(
359
381
 
360
382
  """
361
383
  _require_container_backend()
384
+ enforce_surface_mutation_authorization("postgres.restore", get_postgres_cli_adapter)
362
385
  resolved_user, resolved_db = _postgres_identity(user=user, database=database)
363
386
  cmd = _postgres_exec_base(tty=False)
364
387
  cmd.extend(["psql", "-U", resolved_user, "-d", resolved_db, "-v", "ON_ERROR_STOP=1"])
@@ -423,6 +446,7 @@ def postgres_vacuum(
423
446
 
424
447
  """
425
448
  _require_container_backend()
449
+ enforce_surface_mutation_authorization("postgres.vacuum", get_postgres_cli_adapter)
426
450
  resolved_user, resolved_db = _postgres_identity(user=user, database=database)
427
451
  cmd = _postgres_exec_base(tty=False)
428
452
  cmd.extend(["vacuumdb", "-U", resolved_user])
@@ -24,7 +24,7 @@ compose:
24
24
  # SSL/TLS
25
25
  POSTGRES_SSL_MODE: ${POSTGRES_SSL_MODE:-prefer}
26
26
  ports:
27
- - "${POSTGRES_PORT:-5432}:5432"
27
+ - "${POSTGRES_PORT:-10000}:5432"
28
28
  volumes:
29
29
  - postgres-data:/var/lib/postgresql/data
30
30
  healthcheck:
@@ -45,7 +45,7 @@ env_vars:
45
45
  default: phlo
46
46
  description: PostgreSQL database name
47
47
  POSTGRES_PORT:
48
- default: 5432
48
+ default: 10000
49
49
  description: PostgreSQL host port
50
50
  # SSL/TLS
51
51
  POSTGRES_SSL_MODE:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phlo-postgres
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Postgres service plugin for Phlo
5
5
  Author-email: Phlo Team <team@phlo.dev>
6
6
  License: MIT
@@ -42,7 +42,9 @@ def test_postgres_query_runs_psql(monkeypatch) -> None:
42
42
  ]
43
43
  return CompletedProcess(cmd, 0, stdout="1\n", stderr="")
44
44
 
45
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
45
+ monkeypatch.setattr(
46
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
47
+ )
46
48
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
47
49
  monkeypatch.setattr(
48
50
  "phlo_postgres.cli.compose_base_cmd",
@@ -56,6 +58,20 @@ def test_postgres_query_runs_psql(monkeypatch) -> None:
56
58
  assert result.output == "1\n"
57
59
 
58
60
 
61
+ def test_postgres_query_requires_initialized_services(monkeypatch, tmp_path) -> None:
62
+ phlo_dir = tmp_path / ".phlo"
63
+ phlo_dir.mkdir()
64
+ monkeypatch.chdir(tmp_path)
65
+
66
+ result = CliRunner().invoke(postgres_group, ["query", "SELECT 1"])
67
+
68
+ assert result.exit_code != 0
69
+ assert "Error: Phlo services have not been initialized" in result.output
70
+ assert "Missing: .phlo/docker-compose.yml" in result.output
71
+ assert "Run: phlo services init" in result.output
72
+ assert "couldn't find env file" not in result.output
73
+
74
+
59
75
  def test_postgres_dump_writes_gzip_file(monkeypatch, tmp_path) -> None:
60
76
  output_file = tmp_path / "backup.sql.gz"
61
77
 
@@ -65,7 +81,9 @@ def test_postgres_dump_writes_gzip_file(monkeypatch, tmp_path) -> None:
65
81
  assert cmd[-4:] == ["pg_dump", "-U", "phlo", "phlo"]
66
82
  return CompletedProcess(cmd, 0, stdout="CREATE TABLE test ();", stderr="")
67
83
 
68
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
84
+ monkeypatch.setattr(
85
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
86
+ )
69
87
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
70
88
  monkeypatch.setattr(
71
89
  "phlo_postgres.cli.compose_base_cmd",
@@ -86,7 +104,9 @@ def test_postgres_restore_reads_file(monkeypatch, tmp_path) -> None:
86
104
  input_file.write_text("SELECT 1;", encoding="utf-8")
87
105
  captured: list[tuple[list[str], str]] = []
88
106
 
89
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
107
+ monkeypatch.setattr(
108
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
109
+ )
90
110
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
91
111
  monkeypatch.setattr(
92
112
  "phlo_postgres.cli.compose_base_cmd",
@@ -136,7 +156,9 @@ def test_postgres_vacuum_runs_vacuumdb(monkeypatch) -> None:
136
156
  assert cmd[-5:] == ["vacuumdb", "-U", "phlo", "-z", "phlo"]
137
157
  return CompletedProcess(cmd, 0, stdout="VACUUM\n", stderr="")
138
158
 
139
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
159
+ monkeypatch.setattr(
160
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
161
+ )
140
162
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
141
163
  monkeypatch.setattr(
142
164
  "phlo_postgres.cli.compose_base_cmd",
@@ -153,7 +175,9 @@ def test_postgres_vacuum_runs_vacuumdb(monkeypatch) -> None:
153
175
  def test_postgres_shell_passthrough(monkeypatch) -> None:
154
176
  captured: list[list[str]] = []
155
177
 
156
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
178
+ monkeypatch.setattr(
179
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
180
+ )
157
181
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
158
182
  monkeypatch.setattr(
159
183
  "phlo_postgres.cli.compose_base_cmd",
@@ -197,7 +221,9 @@ def test_postgres_query_timeout(monkeypatch) -> None:
197
221
  return CompletedProcess(cmd, 0, stdout="", stderr="")
198
222
  raise TimeoutExpired(cmd=cmd, timeout=30)
199
223
 
200
- monkeypatch.setattr("phlo_postgres.cli.ensure_phlo_dir", lambda: Path("/tmp/project/.phlo"))
224
+ monkeypatch.setattr(
225
+ "phlo_postgres.cli.ensure_compose_project", lambda: Path("/tmp/project/.phlo")
226
+ )
201
227
  monkeypatch.setattr("phlo_postgres.cli.get_project_name", lambda: "demo")
202
228
  monkeypatch.setattr(
203
229
  "phlo_postgres.cli.compose_base_cmd",
File without changes