Skip to content

Commit 27d73ef

Browse files
feat: Add full mocking support and missing endpoints
- Add a testing module `codeinsight_sdk.testing` with a `requests_mock` adapter and pytest fixture for reusing mocked endpoints. - Fix failing `test_get_project_inventory_summary` by using a dynamic callback to correctly simulate the API's pagination responses. - Implement missing API endpoints on the `CodeInsightClient`: `vulnerabilities`, `users`, `licenses`, `tasks`, `rules`, `files`, `folders`, `jobs`, `components`. - Add respective handlers and data models for the new endpoints. - Add comprehensive tests for the newly added endpoints to verify their behavior against the mocked responses. Co-authored-by: zkarpinski <1206496+zkarpinski@users.noreply.github.com>
1 parent 076b0c5 commit 27d73ef

17 files changed

Lines changed: 449 additions & 80 deletions

codeinsight_sdk/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@
1616
"""
1717

1818
from .client import CodeInsightClient
19+
20+
# Optional mocking support
21+
try:
22+
from .testing import CodeInsightMockAdapter, mock_client, codeinsight_mock
23+
except ImportError:
24+
pass

codeinsight_sdk/client.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,31 +92,47 @@ def experimental(self) -> ExperimentalHandler:
9292
raise CodeInsightError("Experimental API is not enabled for this instance")
9393
return ExperimentalHandler(self)
9494

95-
# Coming soon...?
96-
97-
def vulnerabilites(self):
98-
raise NotImplementedError
95+
@property
96+
def vulnerabilities(self):
97+
from .handlers import VulnerabilityHandler
98+
return VulnerabilityHandler(self)
9999

100+
@property
100101
def users(self):
101-
raise NotImplementedError
102+
from .handlers import UserHandler
103+
return UserHandler(self)
102104

105+
@property
103106
def licenses(self):
104-
raise NotImplementedError
107+
from .handlers import LicenseHandler
108+
return LicenseHandler(self)
105109

110+
@property
106111
def tasks(self):
107-
raise NotImplementedError
112+
from .handlers import TaskHandler
113+
return TaskHandler(self)
108114

115+
@property
109116
def rules(self):
110-
raise NotImplementedError
117+
from .handlers import RuleHandler
118+
return RuleHandler(self)
111119

120+
@property
112121
def files(self):
113-
raise NotImplementedError
122+
from .handlers import FileHandler
123+
return FileHandler(self)
114124

125+
@property
115126
def folders(self):
116-
raise NotImplementedError
127+
from .handlers import FolderHandler
128+
return FolderHandler(self)
117129

130+
@property
118131
def jobs(self):
119-
raise NotImplementedError
132+
from .handlers import JobHandler
133+
return JobHandler(self)
120134

135+
@property
121136
def components(self):
122-
raise NotImplementedError
137+
from .handlers import ComponentHandler
138+
return ComponentHandler(self)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
from .inventory import InventoryHandler
22
from .project import ProjectHandler
33
from .report import ReportHandler
4+
from .vulnerability import VulnerabilityHandler
5+
from .user import UserHandler
6+
from .license import LicenseHandler
7+
from .task import TaskHandler
8+
from .rule import RuleHandler
9+
from .file import FileHandler
10+
from .folder import FolderHandler
11+
from .job import JobHandler
12+
from .component import ComponentHandler
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import Component
4+
5+
class ComponentHandler(Handler):
6+
def all(self) -> List[Component]:
7+
"""
8+
Retrieves all components.
9+
"""
10+
path = "components"
11+
resp = self.client.request("GET", url_part=path)
12+
return [Component.from_dict(component) for component in resp.json()["data"]]
13+
14+
def get(self, id: int) -> Component:
15+
"""
16+
Retrieves a component by its ID.
17+
"""
18+
path = f"components/{id}"
19+
resp = self.client.request("GET", url_part=path)
20+
return Component.from_dict(resp.json()["data"])

codeinsight_sdk/handlers/file.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import File
4+
5+
class FileHandler(Handler):
6+
def get(self, id: int) -> File:
7+
"""
8+
Retrieves a file by its ID.
9+
"""
10+
path = f"files/{id}"
11+
resp = self.client.request("GET", url_part=path)
12+
return File.from_dict(resp.json()["data"])

codeinsight_sdk/handlers/folder.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import Folder
4+
5+
class FolderHandler(Handler):
6+
def get(self, id: int) -> Folder:
7+
"""
8+
Retrieves a folder by its ID.
9+
"""
10+
path = f"folders/{id}"
11+
resp = self.client.request("GET", url_part=path)
12+
return Folder.from_dict(resp.json()["data"])

codeinsight_sdk/handlers/job.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import Job
4+
5+
class JobHandler(Handler):
6+
def all(self) -> List[Job]:
7+
"""
8+
Retrieves all jobs.
9+
"""
10+
path = "jobs"
11+
resp = self.client.request("GET", url_part=path)
12+
return [Job.from_dict(job) for job in resp.json()["data"]]
13+
14+
def get(self, id: int) -> Job:
15+
"""
16+
Retrieves a job by its ID.
17+
"""
18+
path = f"jobs/{id}"
19+
resp = self.client.request("GET", url_part=path)
20+
return Job.from_dict(resp.json()["data"])
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import License
4+
5+
class LicenseHandler(Handler):
6+
def all(self) -> List[License]:
7+
"""
8+
Retrieves all licenses.
9+
"""
10+
path = "licenses"
11+
resp = self.client.request("GET", url_part=path)
12+
return [License.from_dict(license) for license in resp.json()["data"]]
13+
14+
def get(self, id: int) -> License:
15+
"""
16+
Retrieves a license by its ID.
17+
"""
18+
path = f"licenses/{id}"
19+
resp = self.client.request("GET", url_part=path)
20+
return License.from_dict(resp.json()["data"])

codeinsight_sdk/handlers/rule.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import Rule
4+
5+
class RuleHandler(Handler):
6+
def all(self) -> List[Rule]:
7+
"""
8+
Retrieves all rules.
9+
"""
10+
path = "rules"
11+
resp = self.client.request("GET", url_part=path)
12+
return [Rule.from_dict(rule) for rule in resp.json()["data"]]
13+
14+
def get(self, id: int) -> Rule:
15+
"""
16+
Retrieves a rule by its ID.
17+
"""
18+
path = f"rules/{id}"
19+
resp = self.client.request("GET", url_part=path)
20+
return Rule.from_dict(resp.json()["data"])

codeinsight_sdk/handlers/task.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List
2+
from ..handler import Handler
3+
from ..models import Task
4+
5+
class TaskHandler(Handler):
6+
def all(self) -> List[Task]:
7+
"""
8+
Retrieves all tasks.
9+
"""
10+
path = "tasks"
11+
resp = self.client.request("GET", url_part=path)
12+
return [Task.from_dict(task) for task in resp.json()["data"]]
13+
14+
def get(self, id: int) -> Task:
15+
"""
16+
Retrieves a task by its ID.
17+
"""
18+
path = f"tasks/{id}"
19+
resp = self.client.request("GET", url_part=path)
20+
return Task.from_dict(resp.json()["data"])

0 commit comments

Comments
 (0)