From 8535648c3d9242b268e81a4dd1f74d83e7fbd5f2 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Mon, 3 Nov 2025 02:37:40 +0100 Subject: [PATCH] Implemented item indexer on Repository class. --- devops.py | 46 +++++++++++++++++++++++++++------------- harvester.py | 3 ++- tests.py | 60 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 82 insertions(+), 27 deletions(-) diff --git a/devops.py b/devops.py index 43628fd..335969a 100644 --- a/devops.py +++ b/devops.py @@ -1,3 +1,4 @@ +from __future__ import annotations import requests import urllib.parse from uuid import UUID @@ -120,7 +121,7 @@ class Organization(DevOps): list_url="_apis/projects") return self._projects - def __getitem__(self, key: str) -> 'Project': + def __getitem__(self, key: str) -> Project: for project in self.projects: if project.id == key or project.name == key: return project @@ -157,10 +158,18 @@ class Project(DevOps): @property def repositories(self): - return self._entities( - entity_class=Repository, - key_name="id", - list_url=f"{self._id}/_apis/git/repositories") + if not hasattr(self, "_repositories"): + self._repositories = self._entities( + entity_class=Repository, + key_name="id", + list_url=f"{self._id}/_apis/git/repositories") + return self._repositories + + def __getitem__(self, key: str) -> Repository: + for repo in self.repositories: + if repo.id == key or repo.name == key: + return repo + raise KeyError(f"Repository with ID or name '{key}' not found.") @auto_properties({ "name": "name", @@ -209,16 +218,23 @@ class Repository(DevOps): @property def items(self): - # GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/items?api-version=7.1 - return self._entities( - entity_class=Item, - key_name="path", - list_url=f"{self._project.id}/_apis/git/repositories/{self._id}/items", - params={ - "scopePath": "/", - "recursionLevel": "oneLevel" - } - ) + if not hasattr(self, "_items"): + self._items = self._entities( + entity_class=Item, + key_name="path", + list_url=f"{self._project.id}/_apis/git/repositories/{self._id}/items", + params={ + "scopePath": "/", + "recursionLevel": "oneLevel" + } + ) + return self._items + + def __getitem__(self, key: str) -> Item: + for item in self.items: + if item.path == key: + return item + raise KeyError(f"Item with path '{key}' not found.") @auto_properties({ "object_id": "objectId", diff --git a/harvester.py b/harvester.py index 3cff5ac..2ffdcf1 100755 --- a/harvester.py +++ b/harvester.py @@ -4,4 +4,5 @@ from devops import Organization org = Organization("https://dev.azure.com/mcovsandbox") # print(org.projects["bafe0cf1-6c97-4088-864a-ea6dc02b2727"].repositories["feac266f-84d2-41bc-839b-736925a85eaa"].items["/generate-pat.py"]) -print(org["ADO Sandbox"]) +print(org["ADO Sandbox"]["ado-auth-lab"]["/container"].url) +print(org["ADO Sandbox"]["ado-auth-lab"]["/generate-pat.py"].url) diff --git a/tests.py b/tests.py index 5abb1a7..762100b 100755 --- a/tests.py +++ b/tests.py @@ -1,20 +1,19 @@ #!/usr/bin/env python3 import unittest -from devops import Organization, Repository, Project, Item +from azure.identity import DefaultAzureCredential +from devops import DEVOPS_SCOPE, Organization, Repository, Project, Item -# Setup the Organization object outside the test class to speed up tests. +# Get the token outside the test class to speed up tests. # Each Unit test instantinates the class, so doing it here avoids repeated authentication. -org = Organization("https://dev.azure.com/mcovsandbox") +token = DefaultAzureCredential().get_token(DEVOPS_SCOPE).token class Test01(unittest.TestCase): def setUp(self): - self.org = org + self.org = Organization("https://dev.azure.com/mcovsandbox", token=token) def test_01(self): """Listing projects in the organization""" - org = self.org - projects = list(org.projects) - self.assertGreater(len(projects), 0) + self.assertGreater(len(list(self.org.projects)), 0) def test_02(self): """Getting a specific project by ID (object instantiation)""" project = Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727") @@ -33,7 +32,7 @@ class Test01(unittest.TestCase): class Test02(unittest.TestCase): def setUp(self): - self.org = org + self.org = Organization("https://dev.azure.com/mcovsandbox", token=token) def test_01(self): """Listing repositories in a project""" @@ -41,9 +40,29 @@ class Test02(unittest.TestCase): repos = list(project.repositories) self.assertGreater(len(repos), 0) + def test_02(self): + """Getting a specific repository by ID (object instantiation)""" + repo = Repository(Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727"), id="feac266f-84d2-41bc-839b-736925a85eaa") + self.assertEqual(repo.id, "feac266f-84d2-41bc-839b-736925a85eaa") + self.assertEqual(repo.name, "ado-auth-lab") + + def test_03(self): + """Getting a specific repository by name using project indexing (object retrieval)""" + project = Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727") + repo = project["ado-auth-lab"] + self.assertEqual(repo.id, "feac266f-84d2-41bc-839b-736925a85eaa") + self.assertEqual(repo.name, "ado-auth-lab") + + def test_04(self): + """Getting a specific repository by ID using project indexing (object retrieval)""" + project = Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727") + repo = project["feac266f-84d2-41bc-839b-736925a85eaa"] + self.assertEqual(repo.id, "feac266f-84d2-41bc-839b-736925a85eaa") + self.assertEqual(repo.name, "ado-auth-lab") + class Test03(unittest.TestCase): def setUp(self): - self.org = org + self.org = Organization("https://dev.azure.com/mcovsandbox", token=token) def test_01(self): """Getting details of a specific item in a repository""" @@ -58,9 +77,29 @@ class Test03(unittest.TestCase): children = list(docs_item.children) self.assertGreater(len(children), 0) + def test_03(self): + """Getting a specific item from a repository using indexing""" + repo = Repository(Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727"), id="feac266f-84d2-41bc-839b-736925a85eaa") + item = repo["/container"] + self.assertEqual(item.path, "/container") + self.assertTrue(item.is_folder) + + def test_04(self): + """Getting a specific item from a repository using indexing""" + repo = Repository(Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727"), id="feac266f-84d2-41bc-839b-736925a85eaa") + item = repo["/generate-pat.py"] + self.assertEqual(item.path, "/generate-pat.py") + self.assertFalse(item.is_folder) + + def test_05(self): + """Attempting to get a non-existent item from a repository using indexing""" + repo = Repository(Project(self.org, id="bafe0cf1-6c97-4088-864a-ea6dc02b2727"), id="feac266f-84d2-41bc-839b-736925a85eaa") + with self.assertRaises(KeyError): + repo["/non-existent-file.txt"] # This file does not exist + class Test04(unittest.TestCase): def setUp(self): - self.org = org + self.org = Organization("https://dev.azure.com/mcovsandbox", token=token) def test_01(self): """Getting details of a specific item in a repository""" @@ -75,6 +114,5 @@ class Test04(unittest.TestCase): children = list(docs_item.children) self.assertGreater(len(children), 0) - if __name__ == "__main__": unittest.main()