Skip to content

Commit bae6ea9

Browse files
committed
Feature - Add support for reports endpoint
fix - Content key bug minor enhancements. bumped version to v0.0.3
1 parent 431d9af commit bae6ea9

12 files changed

Lines changed: 287 additions & 42 deletions

.github/release-drafter-config.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name-template: 'v$RESOLVED_VERSION 🌈'
2+
tag-template: 'v$RESOLVED_VERSION'
3+
categories:
4+
- title: '🚀 Features'
5+
labels:
6+
- 'feature'
7+
- 'enhancement'
8+
- title: '🐛 Bug Fixes'
9+
labels:
10+
- 'fix'
11+
- 'bugfix'
12+
- 'bug'
13+
- title: '🧰 Maintenance'
14+
label: 'chore'
15+
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
16+
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
17+
version-resolver:
18+
major:
19+
labels:
20+
- 'major'
21+
minor:
22+
labels:
23+
- 'minor'
24+
patch:
25+
labels:
26+
- 'patch'
27+
default: patch
28+
template: |
29+
## What’s Changed
30+
31+
$CHANGES
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Release Drafter
2+
3+
on:
4+
push:
5+
# branches to consider in the event; optional, defaults to all
6+
branches:
7+
- master
8+
# pull_request event is required only for autolabeler
9+
pull_request:
10+
# Only following types are handled by the action, but one can default to all as well
11+
types: [opened, reopened, synchronize]
12+
# pull_request_target event is required for autolabeler to support PRs from forks
13+
# pull_request_target:
14+
# types: [opened, reopened, synchronize]
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
update_release_draft:
21+
permissions:
22+
# write permission is required to create a github release
23+
contents: write
24+
# write permission is required for autolabeler
25+
# otherwise, read permission is required at least
26+
pull-requests: write
27+
runs-on: ubuntu-latest
28+
steps:
29+
# Drafts your next Release notes as Pull Requests are merged into "master"
30+
- uses: release-drafter/release-drafter@v5
31+
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
32+
with:
33+
config-name: release-drafter-config.yml
34+
# disable-autolabeler: true
35+
env:
36+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

codeinsight_sdk/client.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import logging
33

44
from .handlers import Handler
5-
from .models import Project, ProjectInventory
5+
from .models import Project, ProjectInventory, Report
6+
from .exceptions import CodeInsightError
67

78
logger = logging.getLogger(__name__)
89

@@ -29,14 +30,18 @@ def request(self, method, url_part: str, params: dict = None):
2930
if not response.ok:
3031
logger.error(f"Error: {response.status_code} - {response.reason}")
3132
logger.error(response.text)
32-
raise response.raise_for_status()
33+
raise CodeInsightError(response)
3334

3435
return response
3536

3637
@property
3738
def projects(self) -> Handler:
3839
return Handler.create(self, Project)
3940

41+
@property
42+
def reports(self) -> Handler:
43+
return Handler.create(self, Report)
44+
4045

4146
# Coming soon...?
4247
def inventories(self):
@@ -57,9 +62,6 @@ def tasks(self):
5762
def rules(self):
5863
raise NotImplementedError
5964

60-
def reports(self):
61-
raise NotImplementedError
62-
6365
def files(self):
6466
raise NotImplementedError
6567

codeinsight_sdk/errors.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

codeinsight_sdk/exceptions.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
import requests
3+
import json
4+
5+
class GenericError(Exception): #pragma: no cover
6+
"""Generic error class, catch-all for most code insight API errors."""
7+
pass
8+
9+
class NotYetImplementedError(Exception): #pragma: no cover
10+
"""Error class for API features that have not yet been implemented."""
11+
pass
12+
13+
class CodeInsightError(GenericError):
14+
"""Error class for code insight API errors."""
15+
def __init__(self, response: requests.Response):
16+
try:
17+
resp = response.json()
18+
self.code = response.status_code
19+
self.message = resp['Error: ']
20+
self.arguments = resp['Arguments: ']
21+
self.error = resp['Key: ']
22+
self.add_note(f"Arguments: {self.arguments}")
23+
super().__init__("Error: %s - %s" % (self.code, self.message))
24+
25+
except KeyError:
26+
raise Exception(f"Error parsing response: {resp}")
27+
except json.decoder.JSONDecodeError:
28+
raise Exception(f"Error decoding response: {resp}")

codeinsight_sdk/handlers.py

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import List
33

44
from codeinsight_sdk.models import Project, ProjectInventory, ProjectInventoryItem
5+
from codeinsight_sdk.exceptions import CodeInsightError
56

67
class Handler(abc.ABC):
78
def __init__(self, client, cls):
@@ -11,9 +12,12 @@ def __init__(self, client, cls):
1112
@staticmethod
1213
def create(client, cls):
1314
k = cls.__name__
14-
handlers = {"Project": ProjectHandler,
15+
handlers = {"Project": ProjectHandler,
16+
"Report": ReportHandler
1517
}
1618
handler = handlers.get(k)
19+
if handler is None:
20+
raise Exception(f"Handler not found for class '{k}'")
1721
return handler(client, cls)
1822

1923
@abc.abstractmethod
@@ -23,18 +27,34 @@ def get(self):
2327
class ProjectHandler(Handler):
2428
#Note API endpoints switch between projects and project...
2529
def all(self) -> List[Project]:
26-
path = "projects"
27-
resp = self.client.request("GET", url_part=path)
28-
projects = []
29-
for project_data in resp.json()['data']:
30-
projects.append(self.cls.from_dict(project_data))
31-
return projects
30+
"""
31+
Retrieves all projects from the server.
32+
33+
Returns:
34+
A list of Project objects representing all the projects.
35+
"""
36+
37+
path = "projects"
38+
resp = self.client.request("GET", url_part=path)
39+
projects = []
40+
for project_data in resp.json()['data']:
41+
projects.append(self.cls.from_dict(project_data))
42+
return projects
3243

33-
def get(self, id:str) -> Project:
34-
path = f"projects/{id}"
35-
resp = self.client.request("GET", url_part=path)
36-
project_data = resp.json()['data']
37-
return self.cls.from_dict(project_data)
44+
def get(self, id:int) -> Project:
45+
"""
46+
Retrieves a project by its ID.
47+
48+
Args:
49+
id (int): The ID of the project requested.
50+
51+
Returns:
52+
Project: The retrieved project.
53+
"""
54+
path = f"projects/{id}"
55+
resp = self.client.request("GET", url_part=path)
56+
project_data = resp.json()['data']
57+
return self.cls.from_dict(project_data)
3858

3959
def get_id(self, projectName:str) -> int:
4060
"""
@@ -44,30 +64,69 @@ def get_id(self, projectName:str) -> int:
4464
projectName (str): The name of the project.
4565
4666
Returns:
47-
str: The ID of the project.
67+
int: The ID of the project.
4868
"""
4969
path = f"project/id"
5070
params = {"projectName": projectName}
5171
resp = self.client.request("GET", url_part=path, params=params)
52-
projectId = resp.json()['Content']
72+
try:
73+
projectId = resp.json()['Content: '] # Yes, the key is called 'Content: ' ...
74+
except KeyError:
75+
raise CodeInsightError(resp)
76+
#raise Exception(f"Content key not found in response: {resp.json()}")
5377
return projectId
5478

5579
def get_inventory_summary(self, project_id:int) -> List[ProjectInventoryItem]:
56-
path = f"projects/{project_id}/inventorySummary"
57-
resp = self.client.request("GET", url_part=path)
58-
inventory = []
59-
for inv_item in resp.json()['data']:
60-
inventory.append(ProjectInventoryItem.from_dict(inv_item))
61-
return inventory
80+
"""
81+
Retrieves the inventory summary for a specific project.
82+
83+
Args:
84+
project_id (int): The ID of the project.
85+
86+
Returns:
87+
List[ProjectInventoryItem]: A list of ProjectInventoryItem objects representing the inventory summary.
88+
"""
89+
90+
path = f"projects/{project_id}/inventorySummary"
91+
resp = self.client.request("GET", url_part=path)
92+
inventory = []
93+
for inv_item in resp.json()['data']:
94+
inventory.append(ProjectInventoryItem.from_dict(inv_item))
95+
return inventory
6296

6397
def get_inventory(self,project_id:int,
6498
skip_vulnerabilities: bool = False,
6599
published:bool = True,
100+
vendor:str = None,
101+
product:str = None,
102+
page_size: int = 100,
103+
page: int = 1,
104+
review_status: str = None,
105+
alerts: str = None,
106+
include_files: bool = True
66107
) -> ProjectInventory:
67108
path = f"project/inventory/{project_id}"
109+
#TODO: Add support for all parameters
68110
params = {"skipVulnerabilities": skip_vulnerabilities}
69111
resp = self.client.request("GET", url_part=path, params=params)
70112
project_inventory = resp.json()
71113
project_cls = ProjectInventory.from_dict(project_inventory)
72114
return project_cls
73115

116+
117+
class ReportHandler(Handler):
118+
def get(self, id:int):
119+
path = f"reports/{id}"
120+
resp = self.client.request("GET", url_part=path)
121+
report_data = resp.json()['data']
122+
report = self.cls.from_dict(report_data)
123+
return report
124+
125+
def all(self):
126+
path = "reports"
127+
resp = self.client.request("GET", url_part=path)
128+
reports = []
129+
for report_data in resp.json()['data']:
130+
reports.append(self.cls.from_dict(report_data))
131+
return reports
132+

codeinsight_sdk/models.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ class ProjectInventoryItem(DataClassJsonMixin):
4242

4343
@dataclass_json #Trying this style instead of DataClassJsonMixin
4444
@dataclass
45-
class ProjectInventory(DataClassJsonMixin):
45+
class ProjectInventory():
4646
projectId: int
47-
inventoryItems: List[ProjectInventoryItem]
47+
inventoryItems: List[ProjectInventoryItem]
48+
49+
@dataclass
50+
class Report(DataClassJsonMixin):
51+
id: int
52+
name: str
53+
path: str
54+
default: bool
55+
enabled: bool
56+
enableProjectPicker: bool
57+
order: int
58+
createdDateTime: str
59+
updatedDateTime: str

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "codeinsight_sdk"
3-
version = "0.0.2"
3+
version = "0.0.3"
44
description = "A Python client for the Revenera Code Insight"
55
authors = ["Zachary Karpinski <1206496+zkarpinski@users.noreply.github.com>"]
66
readme = "README.md"

0 commit comments

Comments
 (0)