Skip to content

Commit a6f936d

Browse files
committed
adds infrahubctl repository list command
1 parent 34e3cd5 commit a6f936d

File tree

8 files changed

+132
-12
lines changed

8 files changed

+132
-12
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ jobs:
116116

117117

118118
unit-tests:
119+
env:
120+
# workaround for Rich table column width
121+
COLUMNS: 140
119122
strategy:
120123
matrix:
121124
python-version:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
adds `infrahubctl repository list` command

infrahub_sdk/ctl/repository.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
import yaml
77
from pydantic import ValidationError
88
from rich.console import Console
9+
from rich.table import Table
910

1011
from infrahub_sdk.ctl.client import initialize_client
1112

1213
from ..async_typer import AsyncTyper
1314
from ..ctl.exceptions import FileNotValidError
1415
from ..ctl.utils import init_logging
15-
from ..graphql import Mutation
16+
from ..graphql import Mutation, Query
1617
from ..schema.repository import InfrahubRepositoryConfig
1718
from ._file import read_file
1819
from .parameters import CONFIG_PARAM
@@ -102,3 +103,57 @@ async def add(
102103
)
103104

104105
await client.execute_graphql(query=query.render(), branch_name=branch, tracker="mutation-repository-create")
106+
107+
108+
@app.command()
109+
async def list(
110+
branch: str | None = None,
111+
debug: bool = False,
112+
_: str = CONFIG_PARAM,
113+
) -> None:
114+
init_logging(debug=debug)
115+
116+
client = initialize_client(branch=branch)
117+
118+
repo_status_query = {
119+
"CoreGenericRepository": {
120+
"edges": {
121+
"node": {
122+
"__typename": None,
123+
"name": {"value": None},
124+
"operational_status": {"value": None},
125+
"sync_status": {"value": None},
126+
"internal_status": {"value": None},
127+
"... on CoreReadOnlyRepository": {
128+
"ref": {"value": None},
129+
},
130+
}
131+
}
132+
},
133+
}
134+
135+
query = Query(name="GetRepositoryStatus", query=repo_status_query)
136+
resp = await client.execute_graphql(query=query.render(), branch_name=branch, tracker="query-repository-list")
137+
138+
table = Table(title="List of all Repositories")
139+
140+
table.add_column("Name", justify="right", style="cyan", no_wrap=True)
141+
table.add_column("Type")
142+
table.add_column("Operational status")
143+
table.add_column("Sync status")
144+
table.add_column("Internal status")
145+
table.add_column("Ref")
146+
147+
for repository_node in resp["CoreGenericRepository"]["edges"]:
148+
repository = repository_node["node"]
149+
150+
table.add_row(
151+
repository["name"]["value"],
152+
repository["__typename"],
153+
repository["operational_status"]["value"],
154+
repository["sync_status"]["value"],
155+
repository["internal_status"]["value"],
156+
repository["ref"]["value"] if "ref" in repository else "",
157+
)
158+
159+
console.print(table)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
List of all Repositories
2+
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
3+
┃ Name ┃ Type ┃ Operational status ┃ Sync status ┃ Internal status ┃ Ref ┃
4+
┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
5+
│ Demo Edge Repo │ CoreReadOnlyRepository │ unknown │ in-sync │ active │ 5bffc938ba0d00dd111cb19331cdef6aab3729c2 │
6+
│ My Own Repo │ CoreRepository │ in-sync │ in-sync │ active │ │
7+
└────────────────┴────────────────────────┴────────────────────┴─────────────┴─────────────────┴──────────────────────────────────────────┘

tests/helpers/fixtures.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,12 @@ def get_fixtures_dir() -> Path:
55
"""Get the directory which stores fixtures that are common to multiple unit/integration tests."""
66
here = Path(__file__).parent.resolve()
77
return here.parent / "fixtures"
8+
9+
10+
def read_fixture(file_name: str, fixture_subdir: str = ".") -> str:
11+
"""Read the contents of a fixture."""
12+
file_path = get_fixtures_dir() / fixture_subdir / file_name
13+
with file_path.open("r", encoding="utf-8") as fhd:
14+
fixture_contents = fhd.read()
15+
16+
return fixture_contents

tests/unit/ctl/conftest.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,43 @@ async def mock_repositories_query(httpx_mock: HTTPXMock) -> HTTPXMock:
106106
httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json=response1)
107107
httpx_mock.add_response(method="POST", url="http://mock/graphql/cr1234", json=response2)
108108
return httpx_mock
109+
110+
111+
@pytest.fixture
112+
def mock_repositories_list(httpx_mock: HTTPXMock) -> HTTPXMock:
113+
response = {
114+
"data": {
115+
"CoreGenericRepository": {
116+
"edges": [
117+
{
118+
"node": {
119+
"__typename": "CoreReadOnlyRepository",
120+
"name": {"value": "Demo Edge Repo"},
121+
"operational_status": {"value": "unknown"},
122+
"sync_status": {"value": "in-sync"},
123+
"internal_status": {"value": "active"},
124+
"ref": {"value": "5bffc938ba0d00dd111cb19331cdef6aab3729c2"},
125+
}
126+
},
127+
{
128+
"node": {
129+
"__typename": "CoreRepository",
130+
"name": {"value": "My Own Repo"},
131+
"operational_status": {"value": "in-sync"},
132+
"sync_status": {"value": "in-sync"},
133+
"internal_status": {"value": "active"},
134+
}
135+
},
136+
]
137+
}
138+
}
139+
}
140+
141+
httpx_mock.add_response(
142+
method="POST",
143+
status_code=200,
144+
url="http://mock/graphql/main",
145+
json=response,
146+
match_headers={"X-Infrahub-Tracker": "query-repository-list"},
147+
)
148+
return httpx_mock

tests/unit/ctl/test_repository_app.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
from infrahub_sdk.client import InfrahubClient
1010
from infrahub_sdk.ctl.cli_commands import app
11+
from tests.helpers.fixtures import read_fixture
12+
from tests.helpers.utils import strip_color
1113

1214
runner = CliRunner()
1315

@@ -24,11 +26,11 @@ def mock_client() -> mock.Mock:
2426
# ---------------------------------------------------------
2527
# infrahubctl repository command tests
2628
# ---------------------------------------------------------
27-
@mock.patch("infrahub_sdk.ctl.repository.initialize_client")
2829
class TestInfrahubctlRepository:
2930
"""Groups the 'infrahubctl repository' test cases."""
3031

3132
@requires_python_310
33+
@mock.patch("infrahub_sdk.ctl.repository.initialize_client")
3234
def test_repo_no_username(self, mock_init_client, mock_client) -> None:
3335
"""Case allow no username to be passed in and set it as None rather than blank string that fails."""
3436
mock_cred = mock.AsyncMock()
@@ -89,6 +91,7 @@ def test_repo_no_username(self, mock_init_client, mock_client) -> None:
8991
)
9092

9193
@requires_python_310
94+
@mock.patch("infrahub_sdk.ctl.repository.initialize_client")
9295
def test_repo_username(self, mock_init_client, mock_client) -> None:
9396
"""Case allow no username to be passed in and set it as None rather than blank string that fails."""
9497
mock_cred = mock.AsyncMock()
@@ -151,6 +154,7 @@ def test_repo_username(self, mock_init_client, mock_client) -> None:
151154
)
152155

153156
@requires_python_310
157+
@mock.patch("infrahub_sdk.ctl.repository.initialize_client")
154158
def test_repo_readonly_true(self, mock_init_client, mock_client) -> None:
155159
"""Case allow no username to be passed in and set it as None rather than blank string that fails."""
156160
mock_cred = mock.AsyncMock()
@@ -212,6 +216,7 @@ def test_repo_readonly_true(self, mock_init_client, mock_client) -> None:
212216
)
213217

214218
@requires_python_310
219+
@mock.patch("infrahub_sdk.ctl.repository.initialize_client")
215220
def test_repo_description_commit_branch(self, mock_init_client, mock_client) -> None:
216221
"""Case allow no username to be passed in and set it as None rather than blank string that fails."""
217222
mock_cred = mock.AsyncMock()
@@ -278,3 +283,8 @@ def test_repo_description_commit_branch(self, mock_init_client, mock_client) ->
278283
branch_name="develop",
279284
tracker="mutation-repository-create",
280285
)
286+
287+
def test_repo_list(self, mock_repositories_list) -> None:
288+
result = runner.invoke(app, ["repository", "list", "--branch", "main"])
289+
assert result.exit_code == 0
290+
assert strip_color(result.stdout) == read_fixture("output.txt", "integration/test_infrahubctl/repository_list")

tests/unit/ctl/test_transform_app.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from infrahub_sdk.ctl.cli_commands import app
1515
from infrahub_sdk.repository import GitRepoManager
16+
from tests.helpers.fixtures import read_fixture
1617
from tests.helpers.utils import change_directory, strip_color
1718

1819
runner = CliRunner()
@@ -25,14 +26,6 @@
2526
requires_python_310 = pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10 or higher")
2627

2728

28-
def read_fixture(file_name: str, fixture_subdir: str = ".") -> str:
29-
"""Read the contents of a fixture."""
30-
with Path(FIXTURE_BASE_DIR / fixture_subdir / file_name).open("r", encoding="utf-8") as fhd:
31-
fixture_contents = fhd.read()
32-
33-
return fixture_contents
34-
35-
3629
@pytest.fixture
3730
def tags_transform_dir():
3831
temp_dir = tempfile.mkdtemp()
@@ -123,10 +116,12 @@ def test_infrahubctl_transform_cmd_success(httpx_mock: HTTPXMock, tags_transform
123116
httpx_mock.add_response(
124117
method="POST",
125118
url="http://mock/graphql/main",
126-
json=json.loads(read_fixture("case_success_api_return.json", "transform_cmd")),
119+
json=json.loads(read_fixture("case_success_api_return.json", "integration/test_infrahubctl/transform_cmd")),
127120
)
128121

129122
with change_directory(tags_transform_dir):
130123
output = runner.invoke(app, ["transform", "tags_transform", "tag=red"])
131-
assert strip_color(output.stdout) == read_fixture("case_success_output.txt", "transform_cmd")
124+
assert strip_color(output.stdout) == read_fixture(
125+
"case_success_output.txt", "integration/test_infrahubctl/transform_cmd"
126+
)
132127
assert output.exit_code == 0

0 commit comments

Comments
 (0)