Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CM-34777 - Add correlation ID #220

Merged
merged 3 commits into from
Apr 12, 2024
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
Loading