Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cycode/cli/printers/printer_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import click

from cycode.cli.models import CliError, CliResult
from cycode.cyclient.headers import get_correlation_id

if TYPE_CHECKING:
from cycode.cli.models import LocalScanResult
Expand Down Expand Up @@ -46,3 +47,6 @@ def print_exception(self, e: Optional[BaseException] = None) -> None:
message = f'Error: {traceback_message}'

click.secho(message, err=True, fg=self.RED_COLOR_NAME)

correlation_message = f'Correlation ID: {get_correlation_id()}'
click.secho(correlation_message, err=True, fg=self.RED_COLOR_NAME)
26 changes: 5 additions & 21 deletions cycode/cyclient/cycode_client_base.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
import platform
from typing import ClassVar, Dict, Optional

from requests import Response, exceptions, request

from cycode import __version__
from cycode.cli.exceptions.custom_exceptions import HttpUnauthorizedError, NetworkError
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
from cycode.cyclient import config, logger


def get_cli_user_agent() -> str:
"""Return base User-Agent of CLI.

Example: CycodeCLI/0.2.3 (OS: Darwin; Arch: arm64; Python: 3.8.16; InstallID: *uuid4*)
"""
app_name = 'CycodeCLI'
version = __version__

os = platform.system()
arch = platform.machine()
python_version = platform.python_version()

install_id = ConfigurationManager().get_or_create_installation_id()

return f'{app_name}/{version} (OS: {os}; Arch: {arch}; Python: {python_version}; InstallID: {install_id})'
from cycode.cyclient.headers import get_cli_user_agent, get_correlation_id


class CycodeClientBase:
MANDATORY_HEADERS: ClassVar[Dict[str, str]] = {'User-Agent': get_cli_user_agent()}
MANDATORY_HEADERS: ClassVar[Dict[str, str]] = {
'User-Agent': get_cli_user_agent(),
'X-Correlation-Id': get_correlation_id(),
}

def __init__(self, api_url: str) -> None:
self.timeout = config.timeout
Expand Down
46 changes: 46 additions & 0 deletions cycode/cyclient/headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import platform
from typing import Optional
from uuid import uuid4

from cycode import __version__
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
from cycode.cyclient import logger


def get_cli_user_agent() -> str:
"""Return base User-Agent of CLI.

Example: CycodeCLI/0.2.3 (OS: Darwin; Arch: arm64; Python: 3.8.16; InstallID: *uuid4*)
"""
app_name = 'CycodeCLI'
version = __version__

os = platform.system()
arch = platform.machine()
python_version = platform.python_version()

install_id = ConfigurationManager().get_or_create_installation_id()

return f'{app_name}/{version} (OS: {os}; Arch: {arch}; Python: {python_version}; InstallID: {install_id})'


class _CorrelationId:
_id: Optional[str] = None

def get_correlation_id(self) -> str:
"""Get correlation ID.

Notes:
Used across all requests to correlate logs and metrics.
It doesn't depend on client instances.
Lifetime is the same as the process.
"""
if self._id is None:
# example: 16fd2706-8baf-433b-82eb-8c7fada847da
self._id = str(uuid4())
logger.debug(f'Correlation ID: {self._id}')

return self._id


get_correlation_id = _CorrelationId().get_correlation_id
2 changes: 1 addition & 1 deletion tests/cli/exceptions/test_handle_scan_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_handle_exception_verbose(monkeypatch: 'MonkeyPatch') -> None:
ctx = click.Context(click.Command('path'), obj={'verbose': True, 'output': 'text'})

def mock_secho(msg: str, *_, **__) -> None:
assert 'Error:' in msg
assert 'Error:' in msg or 'Correlation ID:' in msg

monkeypatch.setattr(click, 'secho', mock_secho)

Expand Down
4 changes: 3 additions & 1 deletion tests/cyclient/test_client_base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from cycode.cyclient import config
from cycode.cyclient.cycode_client_base import CycodeClientBase, get_cli_user_agent
from cycode.cyclient.cycode_client_base import CycodeClientBase
from cycode.cyclient.headers import get_cli_user_agent, get_correlation_id


def test_mandatory_headers() -> None:
expected_headers = {
'User-Agent': get_cli_user_agent(),
'X-Correlation-Id': get_correlation_id(),
}

client = CycodeClientBase(config.cycode_api_url)
Expand Down