summaryrefslogtreecommitdiff
path: root/gitlab/types.py
blob: 14883c6ad5a9380cb220d9edcb5c794f5afc2d67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import dataclasses
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING


@dataclasses.dataclass(frozen=True)
class RequiredOptional:
    required: Tuple[str, ...] = ()
    optional: Tuple[str, ...] = ()
    exclusive: Tuple[str, ...] = ()

    def validate_attrs(
        self,
        *,
        data: Dict[str, Any],
        excludes: Optional[List[str]] = None,
    ) -> None:
        if excludes is None:
            excludes = []

        if self.required:
            required = [k for k in self.required if k not in excludes]
            missing = [attr for attr in required if attr not in data]
            if missing:
                raise AttributeError(f"Missing attributes: {', '.join(missing)}")

        if self.exclusive:
            exclusives = [attr for attr in data if attr in self.exclusive]
            if len(exclusives) > 1:
                raise AttributeError(
                    f"Provide only one of these attributes: {', '.join(exclusives)}"
                )
            if not exclusives:
                raise AttributeError(
                    f"Must provide one of these attributes: "
                    f"{', '.join(self.exclusive)}"
                )


class GitlabAttribute:
    def __init__(self, value: Any = None) -> None:
        self._value = value

    def get(self) -> Any:
        return self._value

    def set_from_cli(self, cli_value: Any) -> None:
        self._value = cli_value

    def get_for_api(self, *, key: str) -> Tuple[str, Any]:
        return (key, self._value)


class _ListArrayAttribute(GitlabAttribute):
    """Helper class to support `list` / `array` types."""

    def set_from_cli(self, cli_value: str) -> None:
        if not cli_value.strip():
            self._value = []
        else:
            self._value = [item.strip() for item in cli_value.split(",")]

    def get_for_api(self, *, key: str) -> Tuple[str, str]:
        # Do not comma-split single value passed as string
        if isinstance(self._value, str):
            return (key, self._value)

        if TYPE_CHECKING:
            assert isinstance(self._value, list)
        return (key, ",".join([str(x) for x in self._value]))


class ArrayAttribute(_ListArrayAttribute):
    """To support `array` types as documented in
    https://docs.gitlab.com/ee/api/#array"""

    def get_for_api(self, *, key: str) -> Tuple[str, Any]:
        if isinstance(self._value, str):
            return (f"{key}[]", self._value)

        if TYPE_CHECKING:
            assert isinstance(self._value, list)
        return (f"{key}[]", self._value)


class CommaSeparatedListAttribute(_ListArrayAttribute):
    """For values which are sent to the server as a Comma Separated Values
    (CSV) string.  We allow them to be specified as a list and we convert it
    into a CSV"""


class LowercaseStringAttribute(GitlabAttribute):
    def get_for_api(self, *, key: str) -> Tuple[str, str]:
        return (key, str(self._value).lower())


class FileAttribute(GitlabAttribute):
    @staticmethod
    def get_file_name(attr_name: Optional[str] = None) -> Optional[str]:
        return attr_name


class ImageAttribute(FileAttribute):
    @staticmethod
    def get_file_name(attr_name: Optional[str] = None) -> str:
        return f"{attr_name}.png" if attr_name else "image.png"