diff --git a/visual_regression_tracker/__init__.py b/visual_regression_tracker/__init__.py index b37e478..4017d9f 100644 --- a/visual_regression_tracker/__init__.py +++ b/visual_regression_tracker/__init__.py @@ -23,9 +23,10 @@ from .exceptions import VisualRegressionTrackerError, ServerError, TestRunError, MissingConfigurationError from .config import Config from .visualRegressionTracker import VisualRegressionTracker +from .client import Client, ClientConfig __all__ = [ 'Config', 'Build', 'IgnoreArea', 'TestRun', 'TestRunResponse', 'TestRunStatus', 'VisualRegressionTrackerError', 'ServerError', 'TestRunError', 'MissingConfigurationError', - 'VisualRegressionTracker', + 'VisualRegressionTracker', 'Client', 'ClientConfig', ] diff --git a/visual_regression_tracker/client.py b/visual_regression_tracker/client.py new file mode 100644 index 0000000..beeb783 --- /dev/null +++ b/visual_regression_tracker/client.py @@ -0,0 +1,83 @@ +import dataclasses + +from visual_regression_tracker import types +from visual_regression_tracker.visualRegressionTracker import _http_request + +from .config import _from_dict + + +@dataclasses.dataclass +class ClientConfig: + project: str + email: str + password: str + apiUrl: str = "http://localhost:4200" + + +class Client: + def __init__(self, config: ClientConfig): + """ + Creates a new Client + + :param config: The configuration to use. + """ + self.config = config + # retrieve token + response = _http_request( + f"{self.config.apiUrl}/users/login", + "post", + data={ + "email": self.config.email, + "password": self.config.password, + }, + headers={}, + ) + self.headers = {"Authorization": f"Bearer {response['token']}"} + # retrieve project id + self.project = self.get_project(self.config.project) + + def get_projects(self) -> list[types.Project]: + response = _http_request( + f"{self.config.apiUrl}/projects", + "get", + data={}, + headers=self.headers, + ) + projects = [_from_dict(item, types.Project) for item in response] + return projects + + def get_project(self, name: str) -> types.Project: + projects = self.get_projects() + for project in projects: + if project.name == name: + return project + raise ValueError(f'Project with name "{name}" not found.') + + def get_builds(self) -> list[types.Build]: + response = _http_request( + f"{self.config.apiUrl}/builds?projectId={self.project.id}&take=1000&skip=0", + "get", + data={}, + headers=self.headers, + ) + builds = [_from_dict(item, types.Build) for item in response["data"]] + return builds + + def get_build( + self, id: str | None = None, ciBuildId: str | None = None + ) -> types.Build: + if (id is None) == (ciBuildId is None): + raise ValueError("Either 'id' or 'ciBuildId' must be provided.") + builds = self.get_builds() + for build in builds: + if ( + id is not None + and build.id == id + or ciBuildId is not None + and build.ciBuildId == ciBuildId + ): + return build + if id is not None: + raise ValueError(f'Build with id "{id}" not found.') + else: + raise ValueError(f'Build with ciBuildId "{ciBuildId}" not found.') diff --git a/visual_regression_tracker/types.py b/visual_regression_tracker/types.py index f74334c..7259a90 100644 --- a/visual_regression_tracker/types.py +++ b/visual_regression_tracker/types.py @@ -24,10 +24,17 @@ class TestRun: ignoreAreas: typing.List[IgnoreArea] = None +@dataclasses.dataclass +class Project: + id: str = None + name: str = None + @dataclasses.dataclass class Build: id: str = None + ciBuildId: str | None = None projectId: str = None + status: str = None class TestRunStatus(enum.Enum):