summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/__init__.py34
-rw-r--r--gitlab/__main__.py4
-rw-r--r--gitlab/__version__.py6
-rw-r--r--gitlab/base.py331
-rw-r--r--gitlab/cli.py260
-rw-r--r--gitlab/client.py1011
-rw-r--r--gitlab/config.py249
-rw-r--r--gitlab/const.py58
-rw-r--r--gitlab/exceptions.py310
-rw-r--r--gitlab/mixins.py928
-rw-r--r--gitlab/py.typed0
-rw-r--r--gitlab/types.py64
-rw-r--r--gitlab/utils.py70
-rw-r--r--gitlab/v4/__init__.py0
-rw-r--r--gitlab/v4/cli.py500
-rw-r--r--gitlab/v4/objects/__init__.py77
-rw-r--r--gitlab/v4/objects/access_requests.py35
-rw-r--r--gitlab/v4/objects/appearance.py52
-rw-r--r--gitlab/v4/objects/applications.py20
-rw-r--r--gitlab/v4/objects/audit_events.py57
-rw-r--r--gitlab/v4/objects/award_emojis.py103
-rw-r--r--gitlab/v4/objects/badges.py33
-rw-r--r--gitlab/v4/objects/boards.py59
-rw-r--r--gitlab/v4/objects/branches.py42
-rw-r--r--gitlab/v4/objects/broadcast_messages.py23
-rw-r--r--gitlab/v4/objects/clusters.py98
-rw-r--r--gitlab/v4/objects/commits.py200
-rw-r--r--gitlab/v4/objects/container_registry.py58
-rw-r--r--gitlab/v4/objects/custom_attributes.py41
-rw-r--r--gitlab/v4/objects/deploy_keys.py48
-rw-r--r--gitlab/v4/objects/deploy_tokens.py63
-rw-r--r--gitlab/v4/objects/deployments.py30
-rw-r--r--gitlab/v4/objects/discussions.py69
-rw-r--r--gitlab/v4/objects/environments.py43
-rw-r--r--gitlab/v4/objects/epics.py104
-rw-r--r--gitlab/v4/objects/events.py130
-rw-r--r--gitlab/v4/objects/export_import.py54
-rw-r--r--gitlab/v4/objects/features.py59
-rw-r--r--gitlab/v4/objects/files.py228
-rw-r--r--gitlab/v4/objects/geo_nodes.py93
-rw-r--r--gitlab/v4/objects/groups.py334
-rw-r--r--gitlab/v4/objects/hooks.py114
-rw-r--r--gitlab/v4/objects/issues.py256
-rw-r--r--gitlab/v4/objects/jobs.py190
-rw-r--r--gitlab/v4/objects/keys.py26
-rw-r--r--gitlab/v4/objects/labels.py149
-rw-r--r--gitlab/v4/objects/ldap.py51
-rw-r--r--gitlab/v4/objects/members.py92
-rw-r--r--gitlab/v4/objects/merge_request_approvals.py206
-rw-r--r--gitlab/v4/objects/merge_requests.py439
-rw-r--r--gitlab/v4/objects/milestones.py164
-rw-r--r--gitlab/v4/objects/namespaces.py17
-rw-r--r--gitlab/v4/objects/notes.py169
-rw-r--r--gitlab/v4/objects/notification_settings.py57
-rw-r--r--gitlab/v4/objects/packages.py168
-rw-r--r--gitlab/v4/objects/pages.py32
-rw-r--r--gitlab/v4/objects/personal_access_tokens.py32
-rw-r--r--gitlab/v4/objects/pipelines.py227
-rw-r--r--gitlab/v4/objects/project_access_tokens.py17
-rw-r--r--gitlab/v4/objects/projects.py1047
-rw-r--r--gitlab/v4/objects/push_rules.py50
-rw-r--r--gitlab/v4/objects/releases.py41
-rw-r--r--gitlab/v4/objects/repositories.py207
-rw-r--r--gitlab/v4/objects/runners.py140
-rw-r--r--gitlab/v4/objects/services.py303
-rw-r--r--gitlab/v4/objects/settings.py109
-rw-r--r--gitlab/v4/objects/sidekiq.py83
-rw-r--r--gitlab/v4/objects/snippets.py123
-rw-r--r--gitlab/v4/objects/statistics.py52
-rw-r--r--gitlab/v4/objects/tags.py37
-rw-r--r--gitlab/v4/objects/templates.py51
-rw-r--r--gitlab/v4/objects/todos.py50
-rw-r--r--gitlab/v4/objects/triggers.py19
-rw-r--r--gitlab/v4/objects/users.py514
-rw-r--r--gitlab/v4/objects/variables.py66
-rw-r--r--gitlab/v4/objects/wikis.py41
76 files changed, 0 insertions, 11317 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
deleted file mode 100644
index 7b79f22..0000000
--- a/gitlab/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""Wrapper for the GitLab API."""
-
-import warnings
-
-import gitlab.config # noqa: F401
-from gitlab.__version__ import ( # noqa: F401
- __author__,
- __copyright__,
- __email__,
- __license__,
- __title__,
- __version__,
-)
-from gitlab.client import Gitlab, GitlabList # noqa: F401
-from gitlab.const import * # noqa: F401,F403
-from gitlab.exceptions import * # noqa: F401,F403
-
-warnings.filterwarnings("default", category=DeprecationWarning, module="^gitlab")
diff --git a/gitlab/__main__.py b/gitlab/__main__.py
deleted file mode 100644
index e1a914c..0000000
--- a/gitlab/__main__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import gitlab.cli
-
-if __name__ == "__main__":
- gitlab.cli.main()
diff --git a/gitlab/__version__.py b/gitlab/__version__.py
deleted file mode 100644
index d7e8431..0000000
--- a/gitlab/__version__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-__author__ = "Gauvain Pocentek, python-gitlab team"
-__copyright__ = "Copyright 2013-2019 Gauvain Pocentek, 2019-2021 python-gitlab team"
-__email__ = "gauvainpocentek@gmail.com"
-__license__ = "LGPL3"
-__title__ = "python-gitlab"
-__version__ = "2.10.1"
diff --git a/gitlab/base.py b/gitlab/base.py
deleted file mode 100644
index a4a1ef9..0000000
--- a/gitlab/base.py
+++ /dev/null
@@ -1,331 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import importlib
-from types import ModuleType
-from typing import Any, Dict, Iterable, NamedTuple, Optional, Tuple, Type
-
-from gitlab import types as g_types
-from gitlab.exceptions import GitlabParsingError
-
-from .client import Gitlab, GitlabList
-
-__all__ = [
- "RequiredOptional",
- "RESTObject",
- "RESTObjectList",
- "RESTManager",
-]
-
-
-class RESTObject(object):
- """Represents an object built from server data.
-
- It holds the attributes know from the server, and the updated attributes in
- another. This allows smart updates, if the object allows it.
-
- You can redefine ``_id_attr`` in child classes to specify which attribute
- must be used as uniq ID. ``None`` means that the object can be updated
- without ID in the url.
- """
-
- _id_attr: Optional[str] = "id"
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _short_print_attr: Optional[str] = None
- _updated_attrs: Dict[str, Any]
- manager: "RESTManager"
-
- def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
- if not isinstance(attrs, dict):
- raise GitlabParsingError(
- "Attempted to initialize RESTObject with a non-dictionary value: "
- "{!r}\nThis likely indicates an incorrect or malformed server "
- "response.".format(attrs)
- )
- self.__dict__.update(
- {
- "manager": manager,
- "_attrs": attrs,
- "_updated_attrs": {},
- "_module": importlib.import_module(self.__module__),
- }
- )
- self.__dict__["_parent_attrs"] = self.manager.parent_attrs
- self._create_managers()
-
- def __getstate__(self) -> Dict[str, Any]:
- state = self.__dict__.copy()
- module = state.pop("_module")
- state["_module_name"] = module.__name__
- return state
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- module_name = state.pop("_module_name")
- self.__dict__.update(state)
- self.__dict__["_module"] = importlib.import_module(module_name)
-
- def __getattr__(self, name: str) -> Any:
- try:
- return self.__dict__["_updated_attrs"][name]
- except KeyError:
- try:
- value = self.__dict__["_attrs"][name]
-
- # If the value is a list, we copy it in the _updated_attrs dict
- # because we are not able to detect changes made on the object
- # (append, insert, pop, ...). Without forcing the attr
- # creation __setattr__ is never called, the list never ends up
- # in the _updated_attrs dict, and the update() and save()
- # method never push the new data to the server.
- # See https://github.com/python-gitlab/python-gitlab/issues/306
- #
- # note: _parent_attrs will only store simple values (int) so we
- # don't make this check in the next except block.
- if isinstance(value, list):
- self.__dict__["_updated_attrs"][name] = value[:]
- return self.__dict__["_updated_attrs"][name]
-
- return value
-
- except KeyError:
- try:
- return self.__dict__["_parent_attrs"][name]
- except KeyError:
- raise AttributeError(name)
-
- def __setattr__(self, name: str, value: Any) -> None:
- self.__dict__["_updated_attrs"][name] = value
-
- def __str__(self) -> str:
- data = self._attrs.copy()
- data.update(self._updated_attrs)
- return "%s => %s" % (type(self), data)
-
- def __repr__(self) -> str:
- if self._id_attr:
- return "<%s %s:%s>" % (
- self.__class__.__name__,
- self._id_attr,
- self.get_id(),
- )
- else:
- return "<%s>" % self.__class__.__name__
-
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, RESTObject):
- return NotImplemented
- if self.get_id() and other.get_id():
- return self.get_id() == other.get_id()
- return super(RESTObject, self) == other
-
- def __ne__(self, other: object) -> bool:
- if not isinstance(other, RESTObject):
- return NotImplemented
- if self.get_id() and other.get_id():
- return self.get_id() != other.get_id()
- return super(RESTObject, self) != other
-
- def __dir__(self) -> Iterable[str]:
- return set(self.attributes).union(super(RESTObject, self).__dir__())
-
- def __hash__(self) -> int:
- if not self.get_id():
- return super(RESTObject, self).__hash__()
- return hash(self.get_id())
-
- def _create_managers(self) -> None:
- # NOTE(jlvillal): We are creating our managers by looking at the class
- # annotations. If an attribute is annotated as being a *Manager type
- # then we create the manager and assign it to the attribute.
- for attr, annotation in sorted(self.__annotations__.items()):
- if not isinstance(annotation, (type, str)):
- continue
- if isinstance(annotation, type):
- cls_name = annotation.__name__
- else:
- cls_name = annotation
- # All *Manager classes are used except for the base "RESTManager" class
- if cls_name == "RESTManager" or not cls_name.endswith("Manager"):
- continue
- cls = getattr(self._module, cls_name)
- manager = cls(self.manager.gitlab, parent=self)
- # Since we have our own __setattr__ method, we can't use setattr()
- self.__dict__[attr] = manager
-
- def _update_attrs(self, new_attrs: Dict[str, Any]) -> None:
- self.__dict__["_updated_attrs"] = {}
- self.__dict__["_attrs"] = new_attrs
-
- def get_id(self) -> Any:
- """Returns the id of the resource."""
- if self._id_attr is None or not hasattr(self, self._id_attr):
- return None
- return getattr(self, self._id_attr)
-
- @property
- def attributes(self) -> Dict[str, Any]:
- d = self.__dict__["_updated_attrs"].copy()
- d.update(self.__dict__["_attrs"])
- d.update(self.__dict__["_parent_attrs"])
- return d
-
-
-class RESTObjectList(object):
- """Generator object representing a list of RESTObject's.
-
- This generator uses the Gitlab pagination system to fetch new data when
- required.
-
- Note: you should not instantiate such objects, they are returned by calls
- to RESTManager.list()
-
- Args:
- manager: Manager to attach to the created objects
- obj_cls: Type of objects to create from the json data
- _list: A GitlabList object
- """
-
- def __init__(
- self, manager: "RESTManager", obj_cls: Type[RESTObject], _list: GitlabList
- ) -> None:
- """Creates an objects list from a GitlabList.
-
- You should not create objects of this type, but use managers list()
- methods instead.
-
- Args:
- manager: the RESTManager to attach to the objects
- obj_cls: the class of the created objects
- _list: the GitlabList holding the data
- """
- self.manager = manager
- self._obj_cls = obj_cls
- self._list = _list
-
- def __iter__(self) -> "RESTObjectList":
- return self
-
- def __len__(self) -> int:
- return len(self._list)
-
- def __next__(self) -> RESTObject:
- return self.next()
-
- def next(self) -> RESTObject:
- data = self._list.next()
- return self._obj_cls(self.manager, data)
-
- @property
- def current_page(self) -> int:
- """The current page number."""
- return self._list.current_page
-
- @property
- def prev_page(self) -> Optional[int]:
- """The previous page number.
-
- If None, the current page is the first.
- """
- return self._list.prev_page
-
- @property
- def next_page(self) -> Optional[int]:
- """The next page number.
-
- If None, the current page is the last.
- """
- return self._list.next_page
-
- @property
- def per_page(self) -> int:
- """The number of items per page."""
- return self._list.per_page
-
- @property
- def total_pages(self) -> int:
- """The total number of pages."""
- return self._list.total_pages
-
- @property
- def total(self) -> int:
- """The total number of items."""
- return self._list.total
-
-
-class RequiredOptional(NamedTuple):
- required: Tuple[str, ...] = tuple()
- optional: Tuple[str, ...] = tuple()
-
-
-class RESTManager(object):
- """Base class for CRUD operations on objects.
-
- Derived class must define ``_path`` and ``_obj_cls``.
-
- ``_path``: Base URL path on which requests will be sent (e.g. '/projects')
- ``_obj_cls``: The class of objects that will be created
- """
-
- _create_attrs: RequiredOptional = RequiredOptional()
- _update_attrs: RequiredOptional = RequiredOptional()
- _path: Optional[str] = None
- _obj_cls: Optional[Type[RESTObject]] = None
- _from_parent_attrs: Dict[str, Any] = {}
- _types: Dict[str, Type[g_types.GitlabAttribute]] = {}
-
- _computed_path: Optional[str]
- _parent: Optional[RESTObject]
- _parent_attrs: Dict[str, Any]
- gitlab: Gitlab
-
- def __init__(self, gl: Gitlab, parent: Optional[RESTObject] = None) -> None:
- """REST manager constructor.
-
- Args:
- gl (Gitlab): :class:`~gitlab.Gitlab` connection to use to make
- requests.
- parent: REST object to which the manager is attached.
- """
- self.gitlab = gl
- self._parent = parent # for nested managers
- self._computed_path = self._compute_path()
-
- @property
- def parent_attrs(self) -> Optional[Dict[str, Any]]:
- return self._parent_attrs
-
- def _compute_path(self, path: Optional[str] = None) -> Optional[str]:
- self._parent_attrs = {}
- if path is None:
- path = self._path
- if path is None:
- return None
- if self._parent is None or not self._from_parent_attrs:
- return path
-
- data = {
- self_attr: getattr(self._parent, parent_attr, None)
- for self_attr, parent_attr in self._from_parent_attrs.items()
- }
- self._parent_attrs = data
- return path % data
-
- @property
- def path(self) -> Optional[str]:
- return self._computed_path
diff --git a/gitlab/cli.py b/gitlab/cli.py
deleted file mode 100644
index c053a38..0000000
--- a/gitlab/cli.py
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import argparse
-import functools
-import re
-import sys
-from types import ModuleType
-from typing import Any, Callable, cast, Dict, Optional, Tuple, Type, TypeVar, Union
-
-from requests.structures import CaseInsensitiveDict
-
-import gitlab.config
-from gitlab.base import RESTObject
-
-# This regex is based on:
-# https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py
-camel_upperlower_regex = re.compile(r"([A-Z]+)([A-Z][a-z])")
-camel_lowerupper_regex = re.compile(r"([a-z\d])([A-Z])")
-
-# custom_actions = {
-# cls: {
-# action: (mandatory_args, optional_args, in_obj),
-# },
-# }
-custom_actions: Dict[str, Dict[str, Tuple[Tuple[str, ...], Tuple[str, ...], bool]]] = {}
-
-
-# For an explanation of how these type-hints work see:
-# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
-#
-# The goal here is that functions which get decorated will retain their types.
-__F = TypeVar("__F", bound=Callable[..., Any])
-
-
-def register_custom_action(
- cls_names: Union[str, Tuple[str, ...]],
- mandatory: Tuple[str, ...] = tuple(),
- optional: Tuple[str, ...] = tuple(),
- custom_action: Optional[str] = None,
-) -> Callable[[__F], __F]:
- def wrap(f: __F) -> __F:
- @functools.wraps(f)
- def wrapped_f(*args: Any, **kwargs: Any) -> Any:
- return f(*args, **kwargs)
-
- # in_obj defines whether the method belongs to the obj or the manager
- in_obj = True
- if isinstance(cls_names, tuple):
- classes = cls_names
- else:
- classes = (cls_names,)
-
- for cls_name in classes:
- final_name = cls_name
- if cls_name.endswith("Manager"):
- final_name = cls_name.replace("Manager", "")
- in_obj = False
- if final_name not in custom_actions:
- custom_actions[final_name] = {}
-
- action = custom_action or f.__name__.replace("_", "-")
- custom_actions[final_name][action] = (mandatory, optional, in_obj)
-
- return cast(__F, wrapped_f)
-
- return wrap
-
-
-def die(msg: str, e: Optional[Exception] = None) -> None:
- if e:
- msg = "%s (%s)" % (msg, e)
- sys.stderr.write(msg + "\n")
- sys.exit(1)
-
-
-def what_to_cls(what: str, namespace: ModuleType) -> Type[RESTObject]:
- classes = CaseInsensitiveDict(namespace.__dict__)
- lowercase_class = what.replace("-", "")
-
- return classes[lowercase_class]
-
-
-def cls_to_what(cls: RESTObject) -> str:
- dasherized_uppercase = camel_upperlower_regex.sub(r"\1-\2", cls.__name__)
- dasherized_lowercase = camel_lowerupper_regex.sub(r"\1-\2", dasherized_uppercase)
- return dasherized_lowercase.lower()
-
-
-def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
- parser = argparse.ArgumentParser(
- add_help=add_help, description="GitLab API Command Line Interface"
- )
- parser.add_argument("--version", help="Display the version.", action="store_true")
- parser.add_argument(
- "-v",
- "--verbose",
- "--fancy",
- help="Verbose mode (legacy format only)",
- action="store_true",
- )
- parser.add_argument(
- "-d", "--debug", help="Debug mode (display HTTP requests)", action="store_true"
- )
- parser.add_argument(
- "-c",
- "--config-file",
- action="append",
- help="Configuration file to use. Can be used multiple times.",
- )
- parser.add_argument(
- "-g",
- "--gitlab",
- help=(
- "Which configuration section should "
- "be used. If not defined, the default selection "
- "will be used."
- ),
- required=False,
- )
- parser.add_argument(
- "-o",
- "--output",
- help="Output format (v4 only): json|legacy|yaml",
- required=False,
- choices=["json", "legacy", "yaml"],
- default="legacy",
- )
- parser.add_argument(
- "-f",
- "--fields",
- help=(
- "Fields to display in the output (comma "
- "separated). Not used with legacy output"
- ),
- required=False,
- )
-
- return parser
-
-
-def _get_parser() -> argparse.ArgumentParser:
- # NOTE: We must delay import of gitlab.v4.cli until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.cli
-
- parser = _get_base_parser()
- return gitlab.v4.cli.extend_parser(parser)
-
-
-def _parse_value(v: Any) -> Any:
- if isinstance(v, str) and v.startswith("@"):
- # If the user-provided value starts with @, we try to read the file
- # path provided after @ as the real value. Exit on any error.
- try:
- with open(v[1:]) as fl:
- return fl.read()
- except Exception as e:
- sys.stderr.write("%s\n" % e)
- sys.exit(1)
-
- return v
-
-
-def docs() -> argparse.ArgumentParser:
- """
- Provide a statically generated parser for sphinx only, so we don't need
- to provide dummy gitlab config for readthedocs.
- """
- if "sphinx" not in sys.modules:
- sys.exit("Docs parser is only intended for build_sphinx")
-
- return _get_parser()
-
-
-def main() -> None:
- if "--version" in sys.argv:
- print(gitlab.__version__)
- sys.exit(0)
-
- parser = _get_base_parser(add_help=False)
-
- # This first parsing step is used to find the gitlab config to use, and
- # load the propermodule (v3 or v4) accordingly. At that point we don't have
- # any subparser setup
- (options, _) = parser.parse_known_args(sys.argv)
- try:
- config = gitlab.config.GitlabConfigParser(options.gitlab, options.config_file)
- except gitlab.config.ConfigError as e:
- if "--help" in sys.argv or "-h" in sys.argv:
- parser.print_help()
- sys.exit(0)
- sys.exit(e)
- # We only support v4 API at this time
- if config.api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.cli" % config.api_version)
-
- # Now we build the entire set of subcommands and do the complete parsing
- parser = _get_parser()
- try:
- import argcomplete # type: ignore
-
- argcomplete.autocomplete(parser)
- except Exception:
- pass
- args = parser.parse_args()
-
- config_files = args.config_file
- gitlab_id = args.gitlab
- verbose = args.verbose
- output = args.output
- fields = []
- if args.fields:
- fields = [x.strip() for x in args.fields.split(",")]
- debug = args.debug
- action = args.whaction
- what = args.what
-
- args_dict = vars(args)
- # Remove CLI behavior-related args
- for item in (
- "gitlab",
- "config_file",
- "verbose",
- "debug",
- "what",
- "whaction",
- "version",
- "output",
- ):
- args_dict.pop(item)
- args_dict = {k: _parse_value(v) for k, v in args_dict.items() if v is not None}
-
- try:
- gl = gitlab.Gitlab.from_config(gitlab_id, config_files)
- if gl.private_token or gl.oauth_token or gl.job_token:
- gl.auth()
- except Exception as e:
- die(str(e))
-
- if debug:
- gl.enable_debug()
-
- gitlab.v4.cli.run(gl, what, action, args_dict, verbose, output, fields)
diff --git a/gitlab/client.py b/gitlab/client.py
deleted file mode 100644
index 8bec64f..0000000
--- a/gitlab/client.py
+++ /dev/null
@@ -1,1011 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""Wrapper for the GitLab API."""
-
-import time
-from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
-
-import requests
-import requests.utils
-from requests_toolbelt.multipart.encoder import MultipartEncoder # type: ignore
-
-import gitlab.config
-import gitlab.const
-import gitlab.exceptions
-from gitlab import utils
-
-REDIRECT_MSG = (
- "python-gitlab detected a {status_code} ({reason!r}) redirection. You must update "
- "your GitLab URL to the correct URL to avoid issues. The redirection was from: "
- "{source!r} to {target!r}"
-)
-
-
-class Gitlab(object):
- """Represents a GitLab server connection.
-
- Args:
- url (str): The URL of the GitLab server (defaults to https://gitlab.com).
- private_token (str): The user private token
- oauth_token (str): An oauth token
- job_token (str): A CI job token
- ssl_verify (bool|str): Whether SSL certificates should be validated. If
- the value is a string, it is the path to a CA file used for
- certificate validation.
- timeout (float): Timeout to use for requests to the GitLab server.
- http_username (str): Username for HTTP authentication
- http_password (str): Password for HTTP authentication
- api_version (str): Gitlab API version to use (support for 4 only)
- pagination (str): Can be set to 'keyset' to use keyset pagination
- order_by (str): Set order_by globally
- user_agent (str): A custom user agent to use for making HTTP requests.
- retry_transient_errors (bool): Whether to retry after 500, 502, 503, or
- 504 responses. Defaults to False.
- """
-
- def __init__(
- self,
- url: Optional[str] = None,
- private_token: Optional[str] = None,
- oauth_token: Optional[str] = None,
- job_token: Optional[str] = None,
- ssl_verify: Union[bool, str] = True,
- http_username: Optional[str] = None,
- http_password: Optional[str] = None,
- timeout: Optional[float] = None,
- api_version: str = "4",
- session: Optional[requests.Session] = None,
- per_page: Optional[int] = None,
- pagination: Optional[str] = None,
- order_by: Optional[str] = None,
- user_agent: str = gitlab.const.USER_AGENT,
- retry_transient_errors: bool = False,
- ) -> None:
-
- self._api_version = str(api_version)
- self._server_version: Optional[str] = None
- self._server_revision: Optional[str] = None
- self._base_url = self._get_base_url(url)
- self._url = "%s/api/v%s" % (self._base_url, api_version)
- #: Timeout to use for requests to gitlab server
- self.timeout = timeout
- self.retry_transient_errors = retry_transient_errors
- #: Headers that will be used in request to GitLab
- self.headers = {"User-Agent": user_agent}
-
- #: Whether SSL certificates should be validated
- self.ssl_verify = ssl_verify
-
- self.private_token = private_token
- self.http_username = http_username
- self.http_password = http_password
- self.oauth_token = oauth_token
- self.job_token = job_token
- self._set_auth_info()
-
- #: Create a session object for requests
- self.session = session or requests.Session()
-
- self.per_page = per_page
- self.pagination = pagination
- self.order_by = order_by
-
- # We only support v4 API at this time
- if self._api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.objects" % self._api_version)
- # NOTE: We must delay import of gitlab.v4.objects until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.objects
-
- objects = gitlab.v4.objects
- self._objects = objects
-
- self.broadcastmessages = objects.BroadcastMessageManager(self)
- """See :class:`~gitlab.v4.objects.BroadcastMessageManager`"""
- self.deploykeys = objects.DeployKeyManager(self)
- """See :class:`~gitlab.v4.objects.DeployKeyManager`"""
- self.deploytokens = objects.DeployTokenManager(self)
- """See :class:`~gitlab.v4.objects.DeployTokenManager`"""
- self.geonodes = objects.GeoNodeManager(self)
- """See :class:`~gitlab.v4.objects.GeoNodeManager`"""
- self.gitlabciymls = objects.GitlabciymlManager(self)
- """See :class:`~gitlab.v4.objects.GitlabciymlManager`"""
- self.gitignores = objects.GitignoreManager(self)
- """See :class:`~gitlab.v4.objects.GitignoreManager`"""
- self.groups = objects.GroupManager(self)
- """See :class:`~gitlab.v4.objects.GroupManager`"""
- self.hooks = objects.HookManager(self)
- """See :class:`~gitlab.v4.objects.HookManager`"""
- self.issues = objects.IssueManager(self)
- """See :class:`~gitlab.v4.objects.IssueManager`"""
- self.issues_statistics = objects.IssuesStatisticsManager(self)
- """See :class:`~gitlab.v4.objects.IssuesStatisticsManager`"""
- self.keys = objects.KeyManager(self)
- """See :class:`~gitlab.v4.objects.KeyManager`"""
- self.ldapgroups = objects.LDAPGroupManager(self)
- """See :class:`~gitlab.v4.objects.LDAPGroupManager`"""
- self.licenses = objects.LicenseManager(self)
- """See :class:`~gitlab.v4.objects.LicenseManager`"""
- self.namespaces = objects.NamespaceManager(self)
- """See :class:`~gitlab.v4.objects.NamespaceManager`"""
- self.mergerequests = objects.MergeRequestManager(self)
- """See :class:`~gitlab.v4.objects.MergeRequestManager`"""
- self.notificationsettings = objects.NotificationSettingsManager(self)
- """See :class:`~gitlab.v4.objects.NotificationSettingsManager`"""
- self.projects = objects.ProjectManager(self)
- """See :class:`~gitlab.v4.objects.ProjectManager`"""
- self.runners = objects.RunnerManager(self)
- """See :class:`~gitlab.v4.objects.RunnerManager`"""
- self.settings = objects.ApplicationSettingsManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationSettingsManager`"""
- self.appearance = objects.ApplicationAppearanceManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationAppearanceManager`"""
- self.sidekiq = objects.SidekiqManager(self)
- """See :class:`~gitlab.v4.objects.SidekiqManager`"""
- self.snippets = objects.SnippetManager(self)
- """See :class:`~gitlab.v4.objects.SnippetManager`"""
- self.users = objects.UserManager(self)
- """See :class:`~gitlab.v4.objects.UserManager`"""
- self.todos = objects.TodoManager(self)
- """See :class:`~gitlab.v4.objects.TodoManager`"""
- self.dockerfiles = objects.DockerfileManager(self)
- """See :class:`~gitlab.v4.objects.DockerfileManager`"""
- self.events = objects.EventManager(self)
- """See :class:`~gitlab.v4.objects.EventManager`"""
- self.audit_events = objects.AuditEventManager(self)
- """See :class:`~gitlab.v4.objects.AuditEventManager`"""
- self.features = objects.FeatureManager(self)
- """See :class:`~gitlab.v4.objects.FeatureManager`"""
- self.pagesdomains = objects.PagesDomainManager(self)
- """See :class:`~gitlab.v4.objects.PagesDomainManager`"""
- self.user_activities = objects.UserActivitiesManager(self)
- """See :class:`~gitlab.v4.objects.UserActivitiesManager`"""
- self.applications = objects.ApplicationManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationManager`"""
- self.variables = objects.VariableManager(self)
- """See :class:`~gitlab.v4.objects.VariableManager`"""
- self.personal_access_tokens = objects.PersonalAccessTokenManager(self)
- """See :class:`~gitlab.v4.objects.PersonalAccessTokenManager`"""
-
- def __enter__(self) -> "Gitlab":
- return self
-
- def __exit__(self, *args: Any) -> None:
- self.session.close()
-
- def __getstate__(self) -> Dict[str, Any]:
- state = self.__dict__.copy()
- state.pop("_objects")
- return state
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- self.__dict__.update(state)
- # We only support v4 API at this time
- if self._api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.objects" % self._api_version)
- # NOTE: We must delay import of gitlab.v4.objects until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.objects
-
- self._objects = gitlab.v4.objects
-
- @property
- def url(self) -> str:
- """The user-provided server URL."""
- return self._base_url
-
- @property
- def api_url(self) -> str:
- """The computed API base URL."""
- return self._url
-
- @property
- def api_version(self) -> str:
- """The API version used (4 only)."""
- return self._api_version
-
- @classmethod
- def from_config(
- cls, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
- ) -> "Gitlab":
- """Create a Gitlab connection from configuration files.
-
- Args:
- gitlab_id (str): ID of the configuration section.
- config_files list[str]: List of paths to configuration files.
-
- Returns:
- (gitlab.Gitlab): A Gitlab connection.
-
- Raises:
- gitlab.config.GitlabDataError: If the configuration is not correct.
- """
- config = gitlab.config.GitlabConfigParser(
- gitlab_id=gitlab_id, config_files=config_files
- )
- return cls(
- config.url,
- private_token=config.private_token,
- oauth_token=config.oauth_token,
- job_token=config.job_token,
- ssl_verify=config.ssl_verify,
- timeout=config.timeout,
- http_username=config.http_username,
- http_password=config.http_password,
- api_version=config.api_version,
- per_page=config.per_page,
- pagination=config.pagination,
- order_by=config.order_by,
- user_agent=config.user_agent,
- retry_transient_errors=config.retry_transient_errors,
- )
-
- def auth(self) -> None:
- """Performs an authentication using private token.
-
- The `user` attribute will hold a `gitlab.objects.CurrentUser` object on
- success.
- """
- self.user = self._objects.CurrentUserManager(self).get()
-
- def version(self) -> Tuple[str, str]:
- """Returns the version and revision of the gitlab server.
-
- Note that self.version and self.revision will be set on the gitlab
- object.
-
- Returns:
- tuple (str, str): The server version and server revision.
- ('unknown', 'unknwown') if the server doesn't
- perform as expected.
- """
- if self._server_version is None:
- try:
- data = self.http_get("/version")
- if isinstance(data, dict):
- self._server_version = data["version"]
- self._server_revision = data["revision"]
- else:
- self._server_version = "unknown"
- self._server_revision = "unknown"
- except Exception:
- self._server_version = "unknown"
- self._server_revision = "unknown"
-
- return cast(str, self._server_version), cast(str, self._server_revision)
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabVerifyError)
- def lint(self, content: str, **kwargs: Any) -> Tuple[bool, List[str]]:
- """Validate a gitlab CI configuration.
-
- Args:
- content (txt): The .gitlab-ci.yml content
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabVerifyError: If the validation could not be done
-
- Returns:
- tuple: (True, []) if the file is valid, (False, errors(list))
- otherwise
- """
- post_data = {"content": content}
- data = self.http_post("/ci/lint", post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(data, requests.Response)
- return (data["status"] == "valid", data["errors"])
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabMarkdownError)
- def markdown(
- self, text: str, gfm: bool = False, project: Optional[str] = None, **kwargs: Any
- ) -> str:
- """Render an arbitrary Markdown document.
-
- Args:
- text (str): The markdown text to render
- gfm (bool): Render text using GitLab Flavored Markdown. Default is
- False
- project (str): Full path of a project used a context when `gfm` is
- True
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMarkdownError: If the server cannot perform the request
-
- Returns:
- str: The HTML rendering of the markdown text.
- """
- post_data = {"text": text, "gfm": gfm}
- if project is not None:
- post_data["project"] = project
- data = self.http_post("/markdown", post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(data, requests.Response)
- return data["html"]
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabLicenseError)
- def get_license(self, **kwargs: Any) -> Dict[str, Any]:
- """Retrieve information about the current license.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
-
- Returns:
- dict: The current license information
- """
- result = self.http_get("/license", **kwargs)
- if isinstance(result, dict):
- return result
- return {}
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabLicenseError)
- def set_license(self, license: str, **kwargs: Any) -> Dict[str, Any]:
- """Add a new license.
-
- Args:
- license (str): The license string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPostError: If the server cannot perform the request
-
- Returns:
- dict: The new license information
- """
- data = {"license": license}
- result = self.http_post("/license", post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- def _set_auth_info(self) -> None:
- tokens = [
- token
- for token in [self.private_token, self.oauth_token, self.job_token]
- if token
- ]
- if len(tokens) > 1:
- raise ValueError(
- "Only one of private_token, oauth_token or job_token should "
- "be defined"
- )
- if (self.http_username and not self.http_password) or (
- not self.http_username and self.http_password
- ):
- raise ValueError(
- "Both http_username and http_password should " "be defined"
- )
- if self.oauth_token and self.http_username:
- raise ValueError(
- "Only one of oauth authentication or http "
- "authentication should be defined"
- )
-
- self._http_auth = None
- if self.private_token:
- self.headers.pop("Authorization", None)
- self.headers["PRIVATE-TOKEN"] = self.private_token
- self.headers.pop("JOB-TOKEN", None)
-
- if self.oauth_token:
- self.headers["Authorization"] = "Bearer %s" % self.oauth_token
- self.headers.pop("PRIVATE-TOKEN", None)
- self.headers.pop("JOB-TOKEN", None)
-
- if self.job_token:
- self.headers.pop("Authorization", None)
- self.headers.pop("PRIVATE-TOKEN", None)
- self.headers["JOB-TOKEN"] = self.job_token
-
- if self.http_username:
- self._http_auth = requests.auth.HTTPBasicAuth(
- self.http_username, self.http_password
- )
-
- def enable_debug(self) -> None:
- import logging
- from http.client import HTTPConnection # noqa
-
- HTTPConnection.debuglevel = 1 # type: ignore
- logging.basicConfig()
- logging.getLogger().setLevel(logging.DEBUG)
- requests_log = logging.getLogger("requests.packages.urllib3")
- requests_log.setLevel(logging.DEBUG)
- requests_log.propagate = True
-
- def _get_session_opts(self) -> Dict[str, Any]:
- return {
- "headers": self.headers.copy(),
- "auth": self._http_auth,
- "timeout": self.timeout,
- "verify": self.ssl_verify,
- }
-
- def _get_base_url(self, url: Optional[str] = None) -> str:
- """Return the base URL with the trailing slash stripped.
- If the URL is a Falsy value, return the default URL.
- Returns:
- str: The base URL
- """
- if not url:
- return gitlab.const.DEFAULT_URL
-
- return url.rstrip("/")
-
- def _build_url(self, path: str) -> str:
- """Returns the full url from path.
-
- If path is already a url, return it unchanged. If it's a path, append
- it to the stored url.
-
- Returns:
- str: The full URL
- """
- if path.startswith("http://") or path.startswith("https://"):
- return path
- else:
- return "%s%s" % (self._url, path)
-
- def _check_redirects(self, result: requests.Response) -> None:
- # Check the requests history to detect 301/302 redirections.
- # If the initial verb is POST or PUT, the redirected request will use a
- # GET request, leading to unwanted behaviour.
- # If we detect a redirection with a POST or a PUT request, we
- # raise an exception with a useful error message.
- if not result.history:
- return
-
- for item in result.history:
- if item.status_code not in (301, 302):
- continue
- # GET methods can be redirected without issue
- if item.request.method == "GET":
- continue
- target = item.headers.get("location")
- raise gitlab.exceptions.RedirectError(
- REDIRECT_MSG.format(
- status_code=item.status_code,
- reason=item.reason,
- source=item.url,
- target=target,
- )
- )
-
- def _prepare_send_data(
- self,
- files: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- ) -> Tuple[
- Optional[Dict[str, Any]],
- Optional[Union[Dict[str, Any], MultipartEncoder]],
- str,
- ]:
- if files:
- if post_data is None:
- post_data = {}
- else:
- # booleans does not exists for data (neither for MultipartEncoder):
- # cast to string int to avoid: 'bool' object has no attribute 'encode'
- for k, v in post_data.items():
- if isinstance(v, bool):
- post_data[k] = str(int(v))
- post_data["file"] = files.get("file")
- post_data["avatar"] = files.get("avatar")
-
- data = MultipartEncoder(post_data)
- return (None, data, data.content_type)
-
- if raw and post_data:
- return (None, post_data, "application/octet-stream")
-
- return (post_data, None, "application/json")
-
- def http_request(
- self,
- verb: str,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- streamed: bool = False,
- files: Optional[Dict[str, Any]] = None,
- timeout: Optional[float] = None,
- obey_rate_limit: bool = True,
- max_retries: int = 10,
- **kwargs: Any,
- ) -> requests.Response:
- """Make an HTTP request to the Gitlab server.
-
- Args:
- verb (str): The HTTP method to call ('get', 'post', 'put',
- 'delete')
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- streamed (bool): Whether the data should be streamed
- files (dict): The files to send to the server
- timeout (float): The timeout, in seconds, for the request
- obey_rate_limit (bool): Whether to obey 429 Too Many Request
- responses. Defaults to True.
- max_retries (int): Max retries after 429 or transient errors,
- set to -1 to retry forever. Defaults to 10.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- A requests result object.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- """
- query_data = query_data or {}
- url = self._build_url(path)
-
- params: Dict[str, Any] = {}
- utils.copy_dict(params, query_data)
-
- # Deal with kwargs: by default a user uses kwargs to send data to the
- # gitlab server, but this generates problems (python keyword conflicts
- # and python-gitlab/gitlab conflicts).
- # So we provide a `query_parameters` key: if it's there we use its dict
- # value as arguments for the gitlab server, and ignore the other
- # arguments, except pagination ones (per_page and page)
- if "query_parameters" in kwargs:
- utils.copy_dict(params, kwargs["query_parameters"])
- for arg in ("per_page", "page"):
- if arg in kwargs:
- params[arg] = kwargs[arg]
- else:
- utils.copy_dict(params, kwargs)
-
- opts = self._get_session_opts()
-
- verify = opts.pop("verify")
- opts_timeout = opts.pop("timeout")
- # If timeout was passed into kwargs, allow it to override the default
- if timeout is None:
- timeout = opts_timeout
-
- # We need to deal with json vs. data when uploading files
- json, data, content_type = self._prepare_send_data(files, post_data, raw)
- opts["headers"]["Content-type"] = content_type
-
- # Requests assumes that `.` should not be encoded as %2E and will make
- # changes to urls using this encoding. Using a prepped request we can
- # get the desired behavior.
- # The Requests behavior is right but it seems that web servers don't
- # always agree with this decision (this is the case with a default
- # gitlab installation)
- req = requests.Request(verb, url, json=json, data=data, params=params, **opts)
- prepped = self.session.prepare_request(req)
- if TYPE_CHECKING:
- assert prepped.url is not None
- prepped.url = utils.sanitized_url(prepped.url)
- settings = self.session.merge_environment_settings(
- prepped.url, {}, streamed, verify, None
- )
-
- cur_retries = 0
- while True:
- result = self.session.send(prepped, timeout=timeout, **settings)
-
- self._check_redirects(result)
-
- if 200 <= result.status_code < 300:
- return result
-
- retry_transient_errors = kwargs.get(
- "retry_transient_errors", self.retry_transient_errors
- )
- if (429 == result.status_code and obey_rate_limit) or (
- result.status_code in [500, 502, 503, 504] and retry_transient_errors
- ):
- if max_retries == -1 or cur_retries < max_retries:
- wait_time = 2 ** cur_retries * 0.1
- if "Retry-After" in result.headers:
- wait_time = int(result.headers["Retry-After"])
- cur_retries += 1
- time.sleep(wait_time)
- continue
-
- error_message = result.content
- try:
- error_json = result.json()
- for k in ("message", "error"):
- if k in error_json:
- error_message = error_json[k]
- except (KeyError, ValueError, TypeError):
- pass
-
- if result.status_code == 401:
- raise gitlab.exceptions.GitlabAuthenticationError(
- response_code=result.status_code,
- error_message=error_message,
- response_body=result.content,
- )
-
- raise gitlab.exceptions.GitlabHttpError(
- response_code=result.status_code,
- error_message=error_message,
- response_body=result.content,
- )
-
- def http_get(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- streamed: bool = False,
- raw: bool = False,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a GET request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- streamed (bool): Whether the data should be streamed
- raw (bool): If True do not try to parse the output as json
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- A requests result object is streamed is True or the content type is
- not json.
- The parsed json data otherwise.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- result = self.http_request(
- "get", path, query_data=query_data, streamed=streamed, **kwargs
- )
-
- if (
- result.headers["Content-Type"] == "application/json"
- and not streamed
- and not raw
- ):
- try:
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
- else:
- return result
-
- def http_list(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- as_list: Optional[bool] = None,
- **kwargs: Any,
- ) -> Union["GitlabList", List[Dict[str, Any]]]:
- """Make a GET request to the Gitlab server for list-oriented queries.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projects')
- query_data (dict): Data to send as query parameters
- **kwargs: Extra options to send to the server (e.g. sudo, page,
- per_page)
-
- Returns:
- list: A list of the objects returned by the server. If `as_list` is
- False and no pagination-related arguments (`page`, `per_page`,
- `all`) are defined then a GitlabList object (generator) is returned
- instead. This object will make API calls when needed to fetch the
- next items from the server.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
-
- # In case we want to change the default behavior at some point
- as_list = True if as_list is None else as_list
-
- get_all = kwargs.pop("all", False)
- url = self._build_url(path)
-
- page = kwargs.get("page")
-
- if get_all is True and as_list is True:
- return list(GitlabList(self, url, query_data, **kwargs))
-
- if page or as_list is True:
- # pagination requested, we return a list
- return list(GitlabList(self, url, query_data, get_next=False, **kwargs))
-
- # No pagination, generator requested
- return GitlabList(self, url, query_data, **kwargs)
-
- def http_post(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- files: Optional[Dict[str, Any]] = None,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a POST request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- files (dict): The files to send to the server
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The parsed json returned by the server if json is return, else the
- raw content
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- post_data = post_data or {}
-
- result = self.http_request(
- "post",
- path,
- query_data=query_data,
- post_data=post_data,
- files=files,
- **kwargs,
- )
- try:
- if result.headers.get("Content-Type", None) == "application/json":
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
- return result
-
- def http_put(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- files: Optional[Dict[str, Any]] = None,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a PUT request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- files (dict): The files to send to the server
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The parsed json returned by the server.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- post_data = post_data or {}
-
- result = self.http_request(
- "put",
- path,
- query_data=query_data,
- post_data=post_data,
- files=files,
- raw=raw,
- **kwargs,
- )
- try:
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
-
- def http_delete(self, path: str, **kwargs: Any) -> requests.Response:
- """Make a DELETE request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The requests object.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- """
- return self.http_request("delete", path, **kwargs)
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabSearchError)
- def search(
- self, scope: str, search: str, **kwargs: Any
- ) -> Union["GitlabList", List[Dict[str, Any]]]:
- """Search GitLab resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- return self.http_list("/search", query_data=data, **kwargs)
-
-
-class GitlabList(object):
- """Generator representing a list of remote objects.
-
- The object handles the links returned by a query to the API, and will call
- the API again when needed.
- """
-
- def __init__(
- self,
- gl: Gitlab,
- url: str,
- query_data: Dict[str, Any],
- get_next: bool = True,
- **kwargs: Any,
- ) -> None:
- self._gl = gl
-
- # Preserve kwargs for subsequent queries
- self._kwargs = kwargs.copy()
-
- self._query(url, query_data, **self._kwargs)
- self._get_next = get_next
-
- # Remove query_parameters from kwargs, which are saved via the `next` URL
- self._kwargs.pop("query_parameters", None)
-
- def _query(
- self, url: str, query_data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> None:
- query_data = query_data or {}
- result = self._gl.http_request("get", url, query_data=query_data, **kwargs)
- try:
- links = result.links
- if links:
- next_url = links["next"]["url"]
- else:
- next_url = requests.utils.parse_header_links(result.headers["links"])[
- 0
- ]["url"]
- self._next_url = next_url
- except KeyError:
- self._next_url = None
- self._current_page: Optional[Union[str, int]] = result.headers.get("X-Page")
- self._prev_page: Optional[Union[str, int]] = result.headers.get("X-Prev-Page")
- self._next_page: Optional[Union[str, int]] = result.headers.get("X-Next-Page")
- self._per_page: Optional[Union[str, int]] = result.headers.get("X-Per-Page")
- self._total_pages: Optional[Union[str, int]] = result.headers.get(
- "X-Total-Pages"
- )
- self._total: Optional[Union[str, int]] = result.headers.get("X-Total")
-
- try:
- self._data: List[Dict[str, Any]] = result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
-
- self._current = 0
-
- @property
- def current_page(self) -> int:
- """The current page number."""
- if TYPE_CHECKING:
- assert self._current_page is not None
- return int(self._current_page)
-
- @property
- def prev_page(self) -> Optional[int]:
- """The previous page number.
-
- If None, the current page is the first.
- """
- return int(self._prev_page) if self._prev_page else None
-
- @property
- def next_page(self) -> Optional[int]:
- """The next page number.
-
- If None, the current page is the last.
- """
- return int(self._next_page) if self._next_page else None
-
- @property
- def per_page(self) -> int:
- """The number of items per page."""
- if TYPE_CHECKING:
- assert self._per_page is not None
- return int(self._per_page)
-
- @property
- def total_pages(self) -> int:
- """The total number of pages."""
- if TYPE_CHECKING:
- assert self._total_pages is not None
- return int(self._total_pages)
-
- @property
- def total(self) -> int:
- """The total number of items."""
- if TYPE_CHECKING:
- assert self._total is not None
- return int(self._total)
-
- def __iter__(self) -> "GitlabList":
- return self
-
- def __len__(self) -> int:
- if self._total is None:
- return 0
- return int(self._total)
-
- def __next__(self) -> Dict[str, Any]:
- return self.next()
-
- def next(self) -> Dict[str, Any]:
- try:
- item = self._data[self._current]
- self._current += 1
- return item
- except IndexError:
- pass
-
- if self._next_url and self._get_next is True:
- self._query(self._next_url, **self._kwargs)
- return self.next()
-
- raise StopIteration
diff --git a/gitlab/config.py b/gitlab/config.py
deleted file mode 100644
index ba14468..0000000
--- a/gitlab/config.py
+++ /dev/null
@@ -1,249 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import configparser
-import os
-import shlex
-import subprocess
-from os.path import expanduser, expandvars
-from typing import List, Optional, Union
-
-from gitlab.const import USER_AGENT
-
-
-def _env_config() -> List[str]:
- if "PYTHON_GITLAB_CFG" in os.environ:
- return [os.environ["PYTHON_GITLAB_CFG"]]
- return []
-
-
-_DEFAULT_FILES: List[str] = _env_config() + [
- "/etc/python-gitlab.cfg",
- os.path.expanduser("~/.python-gitlab.cfg"),
-]
-
-HELPER_PREFIX = "helper:"
-
-HELPER_ATTRIBUTES = ["job_token", "http_password", "private_token", "oauth_token"]
-
-
-class ConfigError(Exception):
- pass
-
-
-class GitlabIDError(ConfigError):
- pass
-
-
-class GitlabDataError(ConfigError):
- pass
-
-
-class GitlabConfigMissingError(ConfigError):
- pass
-
-
-class GitlabConfigHelperError(ConfigError):
- pass
-
-
-class GitlabConfigParser(object):
- def __init__(
- self, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
- ) -> None:
- self.gitlab_id = gitlab_id
- _files = config_files or _DEFAULT_FILES
- file_exist = False
- for file in _files:
- if os.path.exists(file):
- file_exist = True
- if not file_exist:
- raise GitlabConfigMissingError(
- "Config file not found. \nPlease create one in "
- "one of the following locations: {} \nor "
- "specify a config file using the '-c' parameter.".format(
- ", ".join(_DEFAULT_FILES)
- )
- )
-
- self._config = configparser.ConfigParser()
- self._config.read(_files)
-
- if self.gitlab_id is None:
- try:
- self.gitlab_id = self._config.get("global", "default")
- except Exception as e:
- raise GitlabIDError(
- "Impossible to get the gitlab id (not specified in config file)"
- ) from e
-
- try:
- self.url = self._config.get(self.gitlab_id, "url")
- except Exception as e:
- raise GitlabDataError(
- "Impossible to get gitlab informations from "
- "configuration (%s)" % self.gitlab_id
- ) from e
-
- self.ssl_verify: Union[bool, str] = True
- try:
- self.ssl_verify = self._config.getboolean("global", "ssl_verify")
- except ValueError:
- # Value Error means the option exists but isn't a boolean.
- # Get as a string instead as it should then be a local path to a
- # CA bundle.
- try:
- self.ssl_verify = self._config.get("global", "ssl_verify")
- except Exception:
- pass
- except Exception:
- pass
- try:
- self.ssl_verify = self._config.getboolean(self.gitlab_id, "ssl_verify")
- except ValueError:
- # Value Error means the option exists but isn't a boolean.
- # Get as a string instead as it should then be a local path to a
- # CA bundle.
- try:
- self.ssl_verify = self._config.get(self.gitlab_id, "ssl_verify")
- except Exception:
- pass
- except Exception:
- pass
-
- self.timeout = 60
- try:
- self.timeout = self._config.getint("global", "timeout")
- except Exception:
- pass
- try:
- self.timeout = self._config.getint(self.gitlab_id, "timeout")
- except Exception:
- pass
-
- self.private_token = None
- try:
- self.private_token = self._config.get(self.gitlab_id, "private_token")
- except Exception:
- pass
-
- self.oauth_token = None
- try:
- self.oauth_token = self._config.get(self.gitlab_id, "oauth_token")
- except Exception:
- pass
-
- self.job_token = None
- try:
- self.job_token = self._config.get(self.gitlab_id, "job_token")
- except Exception:
- pass
-
- self.http_username = None
- self.http_password = None
- try:
- self.http_username = self._config.get(self.gitlab_id, "http_username")
- self.http_password = self._config.get(self.gitlab_id, "http_password")
- except Exception:
- pass
-
- self._get_values_from_helper()
-
- self.api_version = "4"
- try:
- self.api_version = self._config.get("global", "api_version")
- except Exception:
- pass
- try:
- self.api_version = self._config.get(self.gitlab_id, "api_version")
- except Exception:
- pass
- if self.api_version not in ("4",):
- raise GitlabDataError("Unsupported API version: %s" % self.api_version)
-
- self.per_page = None
- for section in ["global", self.gitlab_id]:
- try:
- self.per_page = self._config.getint(section, "per_page")
- except Exception:
- pass
- if self.per_page is not None and not 0 <= self.per_page <= 100:
- raise GitlabDataError("Unsupported per_page number: %s" % self.per_page)
-
- self.pagination = None
- try:
- self.pagination = self._config.get(self.gitlab_id, "pagination")
- except Exception:
- pass
-
- self.order_by = None
- try:
- self.order_by = self._config.get(self.gitlab_id, "order_by")
- except Exception:
- pass
-
- self.user_agent = USER_AGENT
- try:
- self.user_agent = self._config.get("global", "user_agent")
- except Exception:
- pass
- try:
- self.user_agent = self._config.get(self.gitlab_id, "user_agent")
- except Exception:
- pass
-
- self.retry_transient_errors = False
- try:
- self.retry_transient_errors = self._config.getboolean(
- "global", "retry_transient_errors"
- )
- except Exception:
- pass
- try:
- self.retry_transient_errors = self._config.getboolean(
- self.gitlab_id, "retry_transient_errors"
- )
- except Exception:
- pass
-
- def _get_values_from_helper(self) -> None:
- """Update attributes that may get values from an external helper program"""
- for attr in HELPER_ATTRIBUTES:
- value = getattr(self, attr)
- if not isinstance(value, str):
- continue
-
- if not value.lower().strip().startswith(HELPER_PREFIX):
- continue
-
- helper = value[len(HELPER_PREFIX) :].strip()
- commmand = [expanduser(expandvars(token)) for token in shlex.split(helper)]
-
- try:
- value = (
- subprocess.check_output(commmand, stderr=subprocess.PIPE)
- .decode("utf-8")
- .strip()
- )
- except subprocess.CalledProcessError as e:
- stderr = e.stderr.decode().strip()
- raise GitlabConfigHelperError(
- f"Failed to read {attr} value from helper "
- f"for {self.gitlab_id}:\n{stderr}"
- ) from e
-
- setattr(self, attr, value)
diff --git a/gitlab/const.py b/gitlab/const.py
deleted file mode 100644
index c57423e..0000000
--- a/gitlab/const.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from gitlab.__version__ import __title__, __version__
-
-DEFAULT_URL: str = "https://gitlab.com"
-
-NO_ACCESS: int = 0
-MINIMAL_ACCESS: int = 5
-GUEST_ACCESS: int = 10
-REPORTER_ACCESS: int = 20
-DEVELOPER_ACCESS: int = 30
-MAINTAINER_ACCESS: int = 40
-OWNER_ACCESS: int = 50
-
-VISIBILITY_PRIVATE: str = "private"
-VISIBILITY_INTERNAL: str = "internal"
-VISIBILITY_PUBLIC: str = "public"
-
-NOTIFICATION_LEVEL_DISABLED: str = "disabled"
-NOTIFICATION_LEVEL_PARTICIPATING: str = "participating"
-NOTIFICATION_LEVEL_WATCH: str = "watch"
-NOTIFICATION_LEVEL_GLOBAL: str = "global"
-NOTIFICATION_LEVEL_MENTION: str = "mention"
-NOTIFICATION_LEVEL_CUSTOM: str = "custom"
-
-# Search scopes
-# all scopes (global, group and project)
-SEARCH_SCOPE_PROJECTS: str = "projects"
-SEARCH_SCOPE_ISSUES: str = "issues"
-SEARCH_SCOPE_MERGE_REQUESTS: str = "merge_requests"
-SEARCH_SCOPE_MILESTONES: str = "milestones"
-SEARCH_SCOPE_WIKI_BLOBS: str = "wiki_blobs"
-SEARCH_SCOPE_COMMITS: str = "commits"
-SEARCH_SCOPE_BLOBS: str = "blobs"
-SEARCH_SCOPE_USERS: str = "users"
-
-# specific global scope
-SEARCH_SCOPE_GLOBAL_SNIPPET_TITLES: str = "snippet_titles"
-
-# specific project scope
-SEARCH_SCOPE_PROJECT_NOTES: str = "notes"
-
-USER_AGENT: str = "{}/{}".format(__title__, __version__)
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
deleted file mode 100644
index 6f2d4c4..0000000
--- a/gitlab/exceptions.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import functools
-from typing import Any, Callable, cast, Optional, Type, TYPE_CHECKING, TypeVar, Union
-
-
-class GitlabError(Exception):
- def __init__(
- self,
- error_message: Union[str, bytes] = "",
- response_code: Optional[int] = None,
- response_body: Optional[bytes] = None,
- ) -> None:
-
- Exception.__init__(self, error_message)
- # Http status code
- self.response_code = response_code
- # Full http response
- self.response_body = response_body
- # Parsed error message from gitlab
- try:
- # if we receive str/bytes we try to convert to unicode/str to have
- # consistent message types (see #616)
- if TYPE_CHECKING:
- assert isinstance(error_message, bytes)
- self.error_message = error_message.decode()
- except Exception:
- if TYPE_CHECKING:
- assert isinstance(error_message, str)
- self.error_message = error_message
-
- def __str__(self) -> str:
- if self.response_code is not None:
- return "{0}: {1}".format(self.response_code, self.error_message)
- else:
- return "{0}".format(self.error_message)
-
-
-class GitlabAuthenticationError(GitlabError):
- pass
-
-
-class RedirectError(GitlabError):
- pass
-
-
-class GitlabParsingError(GitlabError):
- pass
-
-
-class GitlabConnectionError(GitlabError):
- pass
-
-
-class GitlabOperationError(GitlabError):
- pass
-
-
-class GitlabHttpError(GitlabError):
- pass
-
-
-class GitlabListError(GitlabOperationError):
- pass
-
-
-class GitlabGetError(GitlabOperationError):
- pass
-
-
-class GitlabCreateError(GitlabOperationError):
- pass
-
-
-class GitlabUpdateError(GitlabOperationError):
- pass
-
-
-class GitlabDeleteError(GitlabOperationError):
- pass
-
-
-class GitlabSetError(GitlabOperationError):
- pass
-
-
-class GitlabProtectError(GitlabOperationError):
- pass
-
-
-class GitlabTransferProjectError(GitlabOperationError):
- pass
-
-
-class GitlabProjectDeployKeyError(GitlabOperationError):
- pass
-
-
-class GitlabCancelError(GitlabOperationError):
- pass
-
-
-class GitlabPipelineCancelError(GitlabCancelError):
- pass
-
-
-class GitlabRetryError(GitlabOperationError):
- pass
-
-
-class GitlabBuildCancelError(GitlabCancelError):
- pass
-
-
-class GitlabBuildRetryError(GitlabRetryError):
- pass
-
-
-class GitlabBuildPlayError(GitlabRetryError):
- pass
-
-
-class GitlabBuildEraseError(GitlabRetryError):
- pass
-
-
-class GitlabJobCancelError(GitlabCancelError):
- pass
-
-
-class GitlabJobRetryError(GitlabRetryError):
- pass
-
-
-class GitlabJobPlayError(GitlabRetryError):
- pass
-
-
-class GitlabJobEraseError(GitlabRetryError):
- pass
-
-
-class GitlabPipelinePlayError(GitlabRetryError):
- pass
-
-
-class GitlabPipelineRetryError(GitlabRetryError):
- pass
-
-
-class GitlabBlockError(GitlabOperationError):
- pass
-
-
-class GitlabUnblockError(GitlabOperationError):
- pass
-
-
-class GitlabDeactivateError(GitlabOperationError):
- pass
-
-
-class GitlabActivateError(GitlabOperationError):
- pass
-
-
-class GitlabSubscribeError(GitlabOperationError):
- pass
-
-
-class GitlabUnsubscribeError(GitlabOperationError):
- pass
-
-
-class GitlabMRForbiddenError(GitlabOperationError):
- pass
-
-
-class GitlabMRApprovalError(GitlabOperationError):
- pass
-
-
-class GitlabMRRebaseError(GitlabOperationError):
- pass
-
-
-class GitlabMRClosedError(GitlabOperationError):
- pass
-
-
-class GitlabMROnBuildSuccessError(GitlabOperationError):
- pass
-
-
-class GitlabTodoError(GitlabOperationError):
- pass
-
-
-class GitlabTimeTrackingError(GitlabOperationError):
- pass
-
-
-class GitlabUploadError(GitlabOperationError):
- pass
-
-
-class GitlabAttachFileError(GitlabOperationError):
- pass
-
-
-class GitlabImportError(GitlabOperationError):
- pass
-
-
-class GitlabCherryPickError(GitlabOperationError):
- pass
-
-
-class GitlabHousekeepingError(GitlabOperationError):
- pass
-
-
-class GitlabOwnershipError(GitlabOperationError):
- pass
-
-
-class GitlabSearchError(GitlabOperationError):
- pass
-
-
-class GitlabStopError(GitlabOperationError):
- pass
-
-
-class GitlabMarkdownError(GitlabOperationError):
- pass
-
-
-class GitlabVerifyError(GitlabOperationError):
- pass
-
-
-class GitlabRenderError(GitlabOperationError):
- pass
-
-
-class GitlabRepairError(GitlabOperationError):
- pass
-
-
-class GitlabRevertError(GitlabOperationError):
- pass
-
-
-class GitlabLicenseError(GitlabOperationError):
- pass
-
-
-class GitlabFollowError(GitlabOperationError):
- pass
-
-
-class GitlabUnfollowError(GitlabOperationError):
- pass
-
-
-# For an explanation of how these type-hints work see:
-# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
-#
-# The goal here is that functions which get decorated will retain their types.
-__F = TypeVar("__F", bound=Callable[..., Any])
-
-
-def on_http_error(error: Type[Exception]) -> Callable[[__F], __F]:
- """Manage GitlabHttpError exceptions.
-
- This decorator function can be used to catch GitlabHttpError exceptions
- raise specialized exceptions instead.
-
- Args:
- error(Exception): The exception type to raise -- must inherit from
- GitlabError
- """
-
- def wrap(f: __F) -> __F:
- @functools.wraps(f)
- def wrapped_f(*args: Any, **kwargs: Any) -> Any:
- try:
- return f(*args, **kwargs)
- except GitlabHttpError as e:
- raise error(e.error_message, e.response_code, e.response_body) from e
-
- return cast(__F, wrapped_f)
-
- return wrap
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
deleted file mode 100644
index 0c2cd94..0000000
--- a/gitlab/mixins.py
+++ /dev/null
@@ -1,928 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from types import ModuleType
-from typing import (
- Any,
- Callable,
- Dict,
- List,
- Optional,
- Tuple,
- Type,
- TYPE_CHECKING,
- Union,
-)
-
-import requests
-
-import gitlab
-from gitlab import base, cli
-from gitlab import exceptions as exc
-from gitlab import types as g_types
-from gitlab import utils
-
-__all__ = [
- "GetMixin",
- "GetWithoutIdMixin",
- "RefreshMixin",
- "ListMixin",
- "RetrieveMixin",
- "CreateMixin",
- "UpdateMixin",
- "SetMixin",
- "DeleteMixin",
- "CRUDMixin",
- "NoUpdateMixin",
- "SaveMixin",
- "ObjectDeleteMixin",
- "UserAgentDetailMixin",
- "AccessRequestMixin",
- "DownloadMixin",
- "SubscribableMixin",
- "TodoMixin",
- "TimeTrackingMixin",
- "ParticipantsMixin",
- "BadgeRenderMixin",
-]
-
-if TYPE_CHECKING:
- # When running mypy we use these as the base classes
- _RestManagerBase = base.RESTManager
- _RestObjectBase = base.RESTObject
-else:
- _RestManagerBase = object
- _RestObjectBase = object
-
-
-class GetMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _optional_get_attrs: Tuple[str, ...] = ()
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabGetError)
- def get(
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
- ) -> base.RESTObject:
- """Retrieve a single object.
-
- Args:
- id (int or str): ID of the object to retrieve
- lazy (bool): If True, don't request the server, but create a
- shallow object giving access to the managers. This is
- useful if you want to avoid useless calls to the API.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject.
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if not isinstance(id, int):
- id = utils.clean_str_id(id)
- path = "%s/%s" % (self.path, id)
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- if lazy is True:
- if TYPE_CHECKING:
- assert self._obj_cls._id_attr is not None
- return self._obj_cls(self, {self._obj_cls._id_attr: id})
- server_data = self.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- return self._obj_cls(self, server_data)
-
-
-class GetWithoutIdMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _optional_get_attrs: Tuple[str, ...] = ()
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabGetError)
- def get(
- self, id: Optional[Union[int, str]] = None, **kwargs: Any
- ) -> Optional[base.RESTObject]:
- """Retrieve a single object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if TYPE_CHECKING:
- assert self.path is not None
- server_data = self.gitlab.http_get(self.path, **kwargs)
- if server_data is None:
- return None
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class RefreshMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @exc.on_http_error(exc.GitlabGetError)
- def refresh(self, **kwargs: Any) -> None:
- """Refresh a single object from server.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns None (updates the object)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if self._id_attr:
- path = "%s/%s" % (self.manager.path, self.id)
- else:
- if TYPE_CHECKING:
- assert self.manager.path is not None
- path = self.manager.path
- server_data = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class ListMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _list_filters: Tuple[str, ...] = ()
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabListError)
- def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject]]:
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
-
- # Duplicate data to avoid messing with what the user sent us
- data = kwargs.copy()
- if self.gitlab.per_page:
- data.setdefault("per_page", self.gitlab.per_page)
-
- # global keyset pagination
- if self.gitlab.pagination:
- data.setdefault("pagination", self.gitlab.pagination)
-
- if self.gitlab.order_by:
- data.setdefault("order_by", self.gitlab.order_by)
-
- # We get the attributes that need some special transformation
- if self._types:
- for attr_name, type_cls in self._types.items():
- if attr_name in data.keys():
- type_obj = type_cls(data[attr_name])
- data[attr_name] = type_obj.get_for_api()
-
- # Allow to overwrite the path, handy for custom listings
- path = data.pop("path", self.path)
-
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- obj = self.gitlab.http_list(path, **data)
- if isinstance(obj, list):
- return [self._obj_cls(self, item) for item in obj]
- else:
- return base.RESTObjectList(self, self._obj_cls, obj)
-
-
-class RetrieveMixin(ListMixin, GetMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class CreateMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- def _check_missing_create_attrs(self, data: Dict[str, Any]) -> None:
- missing = []
- for attr in self._create_attrs.required:
- if attr not in data:
- missing.append(attr)
- continue
- if missing:
- raise AttributeError("Missing attributes: %s" % ", ".join(missing))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(
- self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> base.RESTObject:
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject: a new instance of the managed object class built with
- the data sent by the server
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- if data is None:
- data = {}
-
- self._check_missing_create_attrs(data)
- files = {}
-
- # We get the attributes that need some special transformation
- if self._types:
- # Duplicate data to avoid messing with what the user sent us
- data = data.copy()
- for attr_name, type_cls in self._types.items():
- if attr_name in data.keys():
- type_obj = type_cls(data[attr_name])
-
- # if the type if FileAttribute we need to pass the data as
- # file
- if isinstance(type_obj, g_types.FileAttribute):
- k = type_obj.get_file_name(attr_name)
- files[attr_name] = (k, data.pop(attr_name))
- else:
- data[attr_name] = type_obj.get_for_api()
-
- # Handle specific URL for creation
- path = kwargs.pop("path", self.path)
- server_data = self.gitlab.http_post(path, post_data=data, files=files, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class UpdateMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- _update_uses_post: bool = False
- gitlab: gitlab.Gitlab
-
- def _check_missing_update_attrs(self, data: Dict[str, Any]) -> None:
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- # Remove the id field from the required list as it was previously moved
- # to the http path.
- required = tuple(
- [k for k in self._update_attrs.required if k != self._obj_cls._id_attr]
- )
- missing = []
- for attr in required:
- if attr not in data:
- missing.append(attr)
- continue
- if missing:
- raise AttributeError("Missing attributes: %s" % ", ".join(missing))
-
- def _get_update_method(
- self,
- ) -> Callable[..., Union[Dict[str, Any], requests.Response]]:
- """Return the HTTP method to use.
-
- Returns:
- object: http_put (default) or http_post
- """
- if self._update_uses_post:
- http_method = self.gitlab.http_post
- else:
- http_method = self.gitlab.http_put
- return http_method
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(
- self,
- id: Optional[Union[str, int]] = None,
- new_data: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> Dict[str, Any]:
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
-
- if id is None:
- path = self.path
- else:
- path = "%s/%s" % (self.path, id)
-
- self._check_missing_update_attrs(new_data)
- files = {}
-
- # We get the attributes that need some special transformation
- if self._types:
- # Duplicate data to avoid messing with what the user sent us
- new_data = new_data.copy()
- for attr_name, type_cls in self._types.items():
- if attr_name in new_data.keys():
- type_obj = type_cls(new_data[attr_name])
-
- # if the type if FileAttribute we need to pass the data as
- # file
- if isinstance(type_obj, g_types.FileAttribute):
- k = type_obj.get_file_name(attr_name)
- files[attr_name] = (k, new_data.pop(attr_name))
- else:
- new_data[attr_name] = type_obj.get_for_api()
-
- http_method = self._get_update_method()
- result = http_method(path, post_data=new_data, files=files, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class SetMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabSetError)
- def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject:
- """Create or update the object.
-
- Args:
- key (str): The key of the object to create/update
- value (str): The value to set for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSetError: If an error occurred
-
- Returns:
- obj: The created/updated attribute
- """
- path = "%s/%s" % (self.path, utils.clean_str_id(key))
- data = {"value": value}
- server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class DeleteMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, id: Union[str, int], **kwargs: Any) -> None:
- """Delete an object on the server.
-
- Args:
- id: ID of the object to delete
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- if id is None:
- path = self.path
- else:
- if not isinstance(id, int):
- id = utils.clean_str_id(id)
- path = "%s/%s" % (self.path, id)
- self.gitlab.http_delete(path, **kwargs)
-
-
-class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class SaveMixin(_RestObjectBase):
- """Mixin for RESTObject's that can be updated."""
-
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- def _get_updated_data(self) -> Dict[str, Any]:
- updated_data = {}
- for attr in self.manager._update_attrs.required:
- # Get everything required, no matter if it's been updated
- updated_data[attr] = getattr(self, attr)
- # Add the updated attributes
- updated_data.update(self._updated_attrs)
-
- return updated_data
-
- def save(self, **kwargs: Any) -> None:
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- updated_data = self._get_updated_data()
- # Nothing to update. Server fails if sent an empty dict.
- if not updated_data:
- return
-
- # call the manager
- obj_id = self.get_id()
- if TYPE_CHECKING:
- assert isinstance(self.manager, UpdateMixin)
- server_data = self.manager.update(obj_id, updated_data, **kwargs)
- if server_data is not None:
- self._update_attrs(server_data)
-
-
-class ObjectDeleteMixin(_RestObjectBase):
- """Mixin for RESTObject's that can be deleted."""
-
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- def delete(self, **kwargs: Any) -> None:
- """Delete the object from the server.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- if TYPE_CHECKING:
- assert isinstance(self.manager, DeleteMixin)
- self.manager.delete(self.get_id(), **kwargs)
-
-
-class UserAgentDetailMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("Snippet", "ProjectSnippet", "ProjectIssue"))
- @exc.on_http_error(exc.GitlabGetError)
- def user_agent_detail(self, **kwargs: Any) -> Dict[str, Any]:
- """Get the user agent detail.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- path = "%s/%s/user_agent_detail" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class AccessRequestMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(
- ("ProjectAccessRequest", "GroupAccessRequest"), tuple(), ("access_level",)
- )
- @exc.on_http_error(exc.GitlabUpdateError)
- def approve(
- self, access_level: int = gitlab.DEVELOPER_ACCESS, **kwargs: Any
- ) -> None:
- """Approve an access request.
-
- Args:
- access_level (int): The access level for the user
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server fails to perform the request
- """
-
- path = "%s/%s/approve" % (self.manager.path, self.id)
- data = {"access_level": access_level}
- server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class DownloadMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("GroupExport", "ProjectExport"))
- @exc.on_http_error(exc.GitlabGetError)
- def download(
- self,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Download the archive of a resource export.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The blob content if streamed is False, None otherwise
- """
- path = "%s/download" % (self.manager.path)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class SubscribableMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(
- ("ProjectIssue", "ProjectMergeRequest", "ProjectLabel", "GroupLabel")
- )
- @exc.on_http_error(exc.GitlabSubscribeError)
- def subscribe(self, **kwargs: Any) -> None:
- """Subscribe to the object notifications.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSubscribeError: If the subscription cannot be done
- """
- path = "%s/%s/subscribe" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
- @cli.register_custom_action(
- ("ProjectIssue", "ProjectMergeRequest", "ProjectLabel", "GroupLabel")
- )
- @exc.on_http_error(exc.GitlabUnsubscribeError)
- def unsubscribe(self, **kwargs: Any) -> None:
- """Unsubscribe from the object notifications.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnsubscribeError: If the unsubscription cannot be done
- """
- path = "%s/%s/unsubscribe" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class TodoMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTodoError)
- def todo(self, **kwargs: Any) -> None:
- """Create a todo associated to the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the todo cannot be set
- """
- path = "%s/%s/todo" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path, **kwargs)
-
-
-class TimeTrackingMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def time_stats(self, **kwargs: Any) -> Dict[str, Any]:
- """Get time stats for the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- # Use the existing time_stats attribute if it exist, otherwise make an
- # API call
- if "time_stats" in self.attributes:
- return self.attributes["time_stats"]
-
- path = "%s/%s/time_stats" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def time_estimate(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
- """Set an estimated time of work for the object.
-
- Args:
- duration (str): Duration in human format (e.g. 3h30)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/time_estimate" % (self.manager.path, self.get_id())
- data = {"duration": duration}
- result = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]:
- """Resets estimated time for the object to 0 seconds.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/reset_time_estimate" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def add_spent_time(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
- """Add time spent working on the object.
-
- Args:
- duration (str): Duration in human format (e.g. 3h30)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/add_spent_time" % (self.manager.path, self.get_id())
- data = {"duration": duration}
- result = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def reset_spent_time(self, **kwargs: Any) -> Dict[str, Any]:
- """Resets the time spent working on the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/reset_spent_time" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class ParticipantsMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectMergeRequest", "ProjectIssue"))
- @exc.on_http_error(exc.GitlabListError)
- def participants(self, **kwargs: Any) -> Dict[str, Any]:
- """List the participants.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of participants
- """
-
- path = "%s/%s/participants" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class BadgeRenderMixin(_RestManagerBase):
- @cli.register_custom_action(
- ("GroupBadgeManager", "ProjectBadgeManager"), ("link_url", "image_url")
- )
- @exc.on_http_error(exc.GitlabRenderError)
- def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]:
- """Preview link_url and image_url after interpolation.
-
- Args:
- link_url (str): URL of the badge link
- image_url (str): URL of the badge image
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRenderError: If the rendering failed
-
- Returns:
- dict: The rendering properties
- """
- path = "%s/render" % self.path
- data = {"link_url": link_url, "image_url": image_url}
- result = self.gitlab.http_get(path, data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
diff --git a/gitlab/py.typed b/gitlab/py.typed
deleted file mode 100644
index e69de29..0000000
--- a/gitlab/py.typed
+++ /dev/null
diff --git a/gitlab/types.py b/gitlab/types.py
deleted file mode 100644
index 22d51e7..0000000
--- a/gitlab/types.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2018 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from typing import Any, Optional, TYPE_CHECKING
-
-
-class GitlabAttribute(object):
- 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) -> Any:
- return self._value
-
-
-class ListAttribute(GitlabAttribute):
- 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) -> str:
- # Do not comma-split single value passed as string
- if isinstance(self._value, str):
- return self._value
-
- if TYPE_CHECKING:
- assert isinstance(self._value, list)
- return ",".join([str(x) for x in self._value])
-
-
-class LowercaseStringAttribute(GitlabAttribute):
- def get_for_api(self) -> str:
- return str(self._value).lower()
-
-
-class FileAttribute(GitlabAttribute):
- def get_file_name(self, attr_name: Optional[str] = None) -> Optional[str]:
- return attr_name
-
-
-class ImageAttribute(FileAttribute):
- def get_file_name(self, attr_name: Optional[str] = None) -> str:
- return "%s.png" % attr_name if attr_name else "image.png"
diff --git a/gitlab/utils.py b/gitlab/utils.py
deleted file mode 100644
index 91b3fb0..0000000
--- a/gitlab/utils.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from typing import Any, Callable, Dict, Optional
-from urllib.parse import quote, urlparse
-
-import requests
-
-
-class _StdoutStream(object):
- def __call__(self, chunk: Any) -> None:
- print(chunk)
-
-
-def response_content(
- response: requests.Response,
- streamed: bool,
- action: Optional[Callable],
- chunk_size: int,
-) -> Optional[bytes]:
- if streamed is False:
- return response.content
-
- if action is None:
- action = _StdoutStream()
-
- for chunk in response.iter_content(chunk_size=chunk_size):
- if chunk:
- action(chunk)
- return None
-
-
-def copy_dict(dest: Dict[str, Any], src: Dict[str, Any]) -> None:
- for k, v in src.items():
- if isinstance(v, dict):
- # Transform dict values to new attributes. For example:
- # custom_attributes: {'foo', 'bar'} =>
- # "custom_attributes['foo']": "bar"
- for dict_k, dict_v in v.items():
- dest["%s[%s]" % (k, dict_k)] = dict_v
- else:
- dest[k] = v
-
-
-def clean_str_id(id: str) -> str:
- return quote(id, safe="")
-
-
-def sanitized_url(url: str) -> str:
- parsed = urlparse(url)
- new_path = parsed.path.replace(".", "%2E")
- return parsed._replace(path=new_path).geturl()
-
-
-def remove_none_from_dict(data: Dict[str, Any]) -> Dict[str, Any]:
- return {k: v for k, v in data.items() if v is not None}
diff --git a/gitlab/v4/__init__.py b/gitlab/v4/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/gitlab/v4/__init__.py
+++ /dev/null
diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py
deleted file mode 100644
index 6986552..0000000
--- a/gitlab/v4/cli.py
+++ /dev/null
@@ -1,500 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import argparse
-import operator
-import sys
-from typing import Any, Dict, List, Optional, Type, TYPE_CHECKING, Union
-
-import gitlab
-import gitlab.base
-import gitlab.v4.objects
-from gitlab import cli
-
-
-class GitlabCLI(object):
- def __init__(
- self, gl: gitlab.Gitlab, what: str, action: str, args: Dict[str, str]
- ) -> None:
- self.cls: Type[gitlab.base.RESTObject] = cli.what_to_cls(
- what, namespace=gitlab.v4.objects
- )
- self.cls_name = self.cls.__name__
- self.what = what.replace("-", "_")
- self.action = action.lower()
- self.gl = gl
- self.args = args
- self.mgr_cls: Union[
- Type[gitlab.mixins.CreateMixin],
- Type[gitlab.mixins.DeleteMixin],
- Type[gitlab.mixins.GetMixin],
- Type[gitlab.mixins.GetWithoutIdMixin],
- Type[gitlab.mixins.ListMixin],
- Type[gitlab.mixins.UpdateMixin],
- ] = getattr(gitlab.v4.objects, self.cls.__name__ + "Manager")
- # We could do something smart, like splitting the manager name to find
- # parents, build the chain of managers to get to the final object.
- # Instead we do something ugly and efficient: interpolate variables in
- # the class _path attribute, and replace the value with the result.
- if TYPE_CHECKING:
- assert self.mgr_cls._path is not None
- self.mgr_cls._path = self.mgr_cls._path % self.args
- self.mgr = self.mgr_cls(gl)
-
- if self.mgr_cls._types:
- for attr_name, type_cls in self.mgr_cls._types.items():
- if attr_name in self.args.keys():
- obj = type_cls()
- obj.set_from_cli(self.args[attr_name])
- self.args[attr_name] = obj.get()
-
- def __call__(self) -> Any:
- # Check for a method that matches object + action
- method = "do_%s_%s" % (self.what, self.action)
- if hasattr(self, method):
- return getattr(self, method)()
-
- # Fallback to standard actions (get, list, create, ...)
- method = "do_%s" % self.action
- if hasattr(self, method):
- return getattr(self, method)()
-
- # Finally try to find custom methods
- return self.do_custom()
-
- def do_custom(self) -> Any:
- in_obj = cli.custom_actions[self.cls_name][self.action][2]
-
- # Get the object (lazy), then act
- if in_obj:
- data = {}
- if self.mgr._from_parent_attrs:
- for k in self.mgr._from_parent_attrs:
- data[k] = self.args[k]
- if not issubclass(self.cls, gitlab.mixins.GetWithoutIdMixin):
- if TYPE_CHECKING:
- assert isinstance(self.cls._id_attr, str)
- data[self.cls._id_attr] = self.args.pop(self.cls._id_attr)
- obj = self.cls(self.mgr, data)
- method_name = self.action.replace("-", "_")
- return getattr(obj, method_name)(**self.args)
- else:
- return getattr(self.mgr, self.action)(**self.args)
-
- def do_project_export_download(self) -> None:
- try:
- project = self.gl.projects.get(int(self.args["project_id"]), lazy=True)
- export_status = project.exports.get()
- if TYPE_CHECKING:
- assert export_status is not None
- data = export_status.download()
- sys.stdout.buffer.write(data)
-
- except Exception as e:
- cli.die("Impossible to download the export", e)
-
- def do_create(self) -> gitlab.base.RESTObject:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.CreateMixin)
- try:
- result = self.mgr.create(self.args)
- except Exception as e:
- cli.die("Impossible to create object", e)
- return result
-
- def do_list(
- self,
- ) -> Union[gitlab.base.RESTObjectList, List[gitlab.base.RESTObject]]:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.ListMixin)
- try:
- result = self.mgr.list(**self.args)
- except Exception as e:
- cli.die("Impossible to list objects", e)
- return result
-
- def do_get(self) -> Optional[gitlab.base.RESTObject]:
- if isinstance(self.mgr, gitlab.mixins.GetWithoutIdMixin):
- try:
- result = self.mgr.get(id=None, **self.args)
- except Exception as e:
- cli.die("Impossible to get object", e)
- return result
-
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.GetMixin)
- assert isinstance(self.cls._id_attr, str)
-
- id = self.args.pop(self.cls._id_attr)
- try:
- result = self.mgr.get(id, lazy=False, **self.args)
- except Exception as e:
- cli.die("Impossible to get object", e)
- return result
-
- def do_delete(self) -> None:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.DeleteMixin)
- assert isinstance(self.cls._id_attr, str)
- id = self.args.pop(self.cls._id_attr)
- try:
- self.mgr.delete(id, **self.args)
- except Exception as e:
- cli.die("Impossible to destroy object", e)
-
- def do_update(self) -> Dict[str, Any]:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.UpdateMixin)
- if issubclass(self.mgr_cls, gitlab.mixins.GetWithoutIdMixin):
- id = None
- else:
- if TYPE_CHECKING:
- assert isinstance(self.cls._id_attr, str)
- id = self.args.pop(self.cls._id_attr)
-
- try:
- result = self.mgr.update(id, self.args)
- except Exception as e:
- cli.die("Impossible to update object", e)
- return result
-
-
-def _populate_sub_parser_by_class(
- cls: Type[gitlab.base.RESTObject], sub_parser: argparse._SubParsersAction
-) -> None:
- mgr_cls_name = cls.__name__ + "Manager"
- mgr_cls = getattr(gitlab.v4.objects, mgr_cls_name)
-
- for action_name in ["list", "get", "create", "update", "delete"]:
- if not hasattr(mgr_cls, action_name):
- continue
-
- sub_parser_action = sub_parser.add_parser(action_name)
- sub_parser_action.add_argument("--sudo", required=False)
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
-
- if action_name == "list":
- for x in mgr_cls._list_filters:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- sub_parser_action.add_argument("--page", required=False)
- sub_parser_action.add_argument("--per-page", required=False)
- sub_parser_action.add_argument("--all", required=False, action="store_true")
-
- if action_name == "delete":
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- if action_name == "get":
- if not issubclass(cls, gitlab.mixins.GetWithoutIdMixin):
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- for x in mgr_cls._optional_get_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if action_name == "create":
- for x in mgr_cls._create_attrs.required:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in mgr_cls._create_attrs.optional:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if action_name == "update":
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- for x in mgr_cls._update_attrs.required:
- if x != cls._id_attr:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
-
- for x in mgr_cls._update_attrs.optional:
- if x != cls._id_attr:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if cls.__name__ in cli.custom_actions:
- name = cls.__name__
- for action_name in cli.custom_actions[name]:
- sub_parser_action = sub_parser.add_parser(action_name)
- # Get the attributes for URL/path construction
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- sub_parser_action.add_argument("--sudo", required=False)
-
- # We need to get the object somehow
- if not issubclass(cls, gitlab.mixins.GetWithoutIdMixin):
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- required, optional, dummy = cli.custom_actions[name][action_name]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in required
- if x != cls._id_attr
- ]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
- for x in optional
- if x != cls._id_attr
- ]
-
- if mgr_cls.__name__ in cli.custom_actions:
- name = mgr_cls.__name__
- for action_name in cli.custom_actions[name]:
- sub_parser_action = sub_parser.add_parser(action_name)
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- sub_parser_action.add_argument("--sudo", required=False)
-
- required, optional, dummy = cli.custom_actions[name][action_name]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in required
- if x != cls._id_attr
- ]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
- for x in optional
- if x != cls._id_attr
- ]
-
-
-def extend_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
- subparsers = parser.add_subparsers(
- title="object", dest="what", help="Object to manipulate."
- )
- subparsers.required = True
-
- # populate argparse for all Gitlab Object
- classes = []
- for cls in gitlab.v4.objects.__dict__.values():
- if not isinstance(cls, type):
- continue
- if issubclass(cls, gitlab.base.RESTManager):
- if cls._obj_cls is not None:
- classes.append(cls._obj_cls)
- classes.sort(key=operator.attrgetter("__name__"))
-
- for cls in classes:
- arg_name = cli.cls_to_what(cls)
- object_group = subparsers.add_parser(arg_name)
-
- object_subparsers = object_group.add_subparsers(
- title="action", dest="whaction", help="Action to execute."
- )
- _populate_sub_parser_by_class(cls, object_subparsers)
- object_subparsers.required = True
-
- return parser
-
-
-def get_dict(
- obj: Union[str, gitlab.base.RESTObject], fields: List[str]
-) -> Union[str, Dict[str, Any]]:
- if isinstance(obj, str):
- return obj
-
- if fields:
- return {k: v for k, v in obj.attributes.items() if k in fields}
- return obj.attributes
-
-
-class JSONPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- import json # noqa
-
- print(json.dumps(d))
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- import json # noqa
-
- print(json.dumps([get_dict(obj, fields) for obj in data]))
-
-
-class YAMLPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- try:
- import yaml # noqa
-
- print(yaml.safe_dump(d, default_flow_style=False))
- except ImportError:
- exit(
- "PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature"
- )
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- try:
- import yaml # noqa
-
- print(
- yaml.safe_dump(
- [get_dict(obj, fields) for obj in data], default_flow_style=False
- )
- )
- except ImportError:
- exit(
- "PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature"
- )
-
-
-class LegacyPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- verbose = kwargs.get("verbose", False)
- padding = kwargs.get("padding", 0)
- obj: Optional[Union[Dict[str, Any], gitlab.base.RESTObject]] = kwargs.get("obj")
- if TYPE_CHECKING:
- assert obj is not None
-
- def display_dict(d: Dict[str, Any], padding: int) -> None:
- for k in sorted(d.keys()):
- v = d[k]
- if isinstance(v, dict):
- print("%s%s:" % (" " * padding, k.replace("_", "-")))
- new_padding = padding + 2
- self.display(v, verbose=True, padding=new_padding, obj=v)
- continue
- print("%s%s: %s" % (" " * padding, k.replace("_", "-"), v))
-
- if verbose:
- if isinstance(obj, dict):
- display_dict(obj, padding)
- return
-
- # not a dict, we assume it's a RESTObject
- if obj._id_attr:
- id = getattr(obj, obj._id_attr, None)
- print("%s: %s" % (obj._id_attr, id))
- attrs = obj.attributes
- if obj._id_attr:
- attrs.pop(obj._id_attr)
- display_dict(attrs, padding)
-
- else:
- if TYPE_CHECKING:
- assert isinstance(obj, gitlab.base.RESTObject)
- if obj._id_attr:
- id = getattr(obj, obj._id_attr)
- print("%s: %s" % (obj._id_attr.replace("_", "-"), id))
- if obj._short_print_attr:
- value = getattr(obj, obj._short_print_attr) or "None"
- value = value.replace("\r", "").replace("\n", " ")
- # If the attribute is a note (ProjectCommitComment) then we do
- # some modifications to fit everything on one line
- line = "%s: %s" % (obj._short_print_attr, value)
- # ellipsize long lines (comments)
- if len(line) > 79:
- line = line[:76] + "..."
- print(line)
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- verbose = kwargs.get("verbose", False)
- for obj in data:
- if isinstance(obj, gitlab.base.RESTObject):
- self.display(get_dict(obj, fields), verbose=verbose, obj=obj)
- else:
- print(obj)
- print("")
-
-
-PRINTERS: Dict[
- str, Union[Type[JSONPrinter], Type[LegacyPrinter], Type[YAMLPrinter]]
-] = {
- "json": JSONPrinter,
- "legacy": LegacyPrinter,
- "yaml": YAMLPrinter,
-}
-
-
-def run(
- gl: gitlab.Gitlab,
- what: str,
- action: str,
- args: Dict[str, Any],
- verbose: bool,
- output: str,
- fields: List[str],
-) -> None:
- g_cli = GitlabCLI(gl=gl, what=what, action=action, args=args)
- data = g_cli()
-
- printer: Union[JSONPrinter, LegacyPrinter, YAMLPrinter] = PRINTERS[output]()
-
- if isinstance(data, dict):
- printer.display(data, verbose=True, obj=data)
- elif isinstance(data, list):
- printer.display_list(data, fields, verbose=verbose)
- elif isinstance(data, gitlab.base.RESTObject):
- printer.display(get_dict(data, fields), verbose=verbose, obj=data)
- elif isinstance(data, str):
- print(data)
- elif isinstance(data, bytes):
- sys.stdout.buffer.write(data)
- elif hasattr(data, "decode"):
- print(data.decode())
diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py
deleted file mode 100644
index c2ff4fb..0000000
--- a/gitlab/v4/objects/__init__.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from .access_requests import *
-from .appearance import *
-from .applications import *
-from .audit_events import *
-from .award_emojis import *
-from .badges import *
-from .boards import *
-from .branches import *
-from .broadcast_messages import *
-from .clusters import *
-from .commits import *
-from .container_registry import *
-from .custom_attributes import *
-from .deploy_keys import *
-from .deploy_tokens import *
-from .deployments import *
-from .discussions import *
-from .environments import *
-from .epics import *
-from .events import *
-from .export_import import *
-from .features import *
-from .files import *
-from .geo_nodes import *
-from .groups import *
-from .hooks import *
-from .issues import *
-from .jobs import *
-from .keys import *
-from .labels import *
-from .ldap import *
-from .members import *
-from .merge_request_approvals import *
-from .merge_requests import *
-from .milestones import *
-from .namespaces import *
-from .notes import *
-from .notification_settings import *
-from .packages import *
-from .pages import *
-from .personal_access_tokens import *
-from .pipelines import *
-from .projects import *
-from .push_rules import *
-from .releases import *
-from .runners import *
-from .services import *
-from .settings import *
-from .sidekiq import *
-from .snippets import *
-from .statistics import *
-from .tags import *
-from .templates import *
-from .todos import *
-from .triggers import *
-from .users import *
-from .variables import *
-from .wikis import *
-
-__all__ = [name for name in dir() if not name.startswith("_")]
diff --git a/gitlab/v4/objects/access_requests.py b/gitlab/v4/objects/access_requests.py
deleted file mode 100644
index 4e3328a..0000000
--- a/gitlab/v4/objects/access_requests.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import (
- AccessRequestMixin,
- CreateMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
-)
-
-__all__ = [
- "GroupAccessRequest",
- "GroupAccessRequestManager",
- "ProjectAccessRequest",
- "ProjectAccessRequestManager",
-]
-
-
-class GroupAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/access_requests"
- _obj_cls = GroupAccessRequest
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/access_requests"
- _obj_cls = ProjectAccessRequest
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/appearance.py b/gitlab/v4/objects/appearance.py
deleted file mode 100644
index a34398e..0000000
--- a/gitlab/v4/objects/appearance.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "ApplicationAppearance",
- "ApplicationAppearanceManager",
-]
-
-
-class ApplicationAppearance(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ApplicationAppearanceManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/application/appearance"
- _obj_cls = ApplicationAppearance
- _update_attrs = RequiredOptional(
- optional=(
- "title",
- "description",
- "logo",
- "header_logo",
- "favicon",
- "new_project_guidelines",
- "header_message",
- "footer_message",
- "message_background_color",
- "message_font_color",
- "email_header_and_footer_enabled",
- ),
- )
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- super(ApplicationAppearanceManager, self).update(id, data, **kwargs)
diff --git a/gitlab/v4/objects/applications.py b/gitlab/v4/objects/applications.py
deleted file mode 100644
index c91dee1..0000000
--- a/gitlab/v4/objects/applications.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Application",
- "ApplicationManager",
-]
-
-
-class Application(ObjectDeleteMixin, RESTObject):
- _url = "/applications"
- _short_print_attr = "name"
-
-
-class ApplicationManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/applications"
- _obj_cls = Application
- _create_attrs = RequiredOptional(
- required=("name", "redirect_uri", "scopes"), optional=("confidential",)
- )
diff --git a/gitlab/v4/objects/audit_events.py b/gitlab/v4/objects/audit_events.py
deleted file mode 100644
index 20ea116..0000000
--- a/gitlab/v4/objects/audit_events.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/audit_events.html
-"""
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "AuditEvent",
- "AuditEventManager",
- "GroupAuditEvent",
- "GroupAuditEventManager",
- "ProjectAuditEvent",
- "ProjectAuditEventManager",
- "ProjectAudit",
- "ProjectAuditManager",
-]
-
-
-class AuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class AuditEventManager(RetrieveMixin, RESTManager):
- _path = "/audit_events"
- _obj_cls = AuditEvent
- _list_filters = ("created_after", "created_before", "entity_type", "entity_id")
-
-
-class GroupAuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class GroupAuditEventManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/audit_events"
- _obj_cls = GroupAuditEvent
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("created_after", "created_before")
-
-
-class ProjectAuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class ProjectAuditEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/audit_events"
- _obj_cls = ProjectAuditEvent
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = ("created_after", "created_before")
-
-
-class ProjectAudit(ProjectAuditEvent):
- pass
-
-
-class ProjectAuditManager(ProjectAuditEventManager):
- pass
diff --git a/gitlab/v4/objects/award_emojis.py b/gitlab/v4/objects/award_emojis.py
deleted file mode 100644
index 1a7aecd..0000000
--- a/gitlab/v4/objects/award_emojis.py
+++ /dev/null
@@ -1,103 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectIssueAwardEmoji",
- "ProjectIssueAwardEmojiManager",
- "ProjectIssueNoteAwardEmoji",
- "ProjectIssueNoteAwardEmojiManager",
- "ProjectMergeRequestAwardEmoji",
- "ProjectMergeRequestAwardEmojiManager",
- "ProjectMergeRequestNoteAwardEmoji",
- "ProjectMergeRequestNoteAwardEmojiManager",
- "ProjectSnippetAwardEmoji",
- "ProjectSnippetAwardEmojiManager",
- "ProjectSnippetNoteAwardEmoji",
- "ProjectSnippetNoteAwardEmojiManager",
-]
-
-
-class ProjectIssueAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/award_emoji"
- _obj_cls = ProjectIssueAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/issues/%(issue_iid)s" "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectIssueNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "issue_iid": "issue_iid",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectMergeRequestAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/award_emoji"
- _obj_cls = ProjectMergeRequestAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s"
- "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectMergeRequestNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "mr_iid": "mr_iid",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/award_emoji"
- _obj_cls = ProjectSnippetAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/snippets/%(snippet_id)s"
- "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectSnippetNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "snippet_id": "snippet_id",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
diff --git a/gitlab/v4/objects/badges.py b/gitlab/v4/objects/badges.py
deleted file mode 100644
index 198f6ea..0000000
--- a/gitlab/v4/objects/badges.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import BadgeRenderMixin, CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupBadge",
- "GroupBadgeManager",
- "ProjectBadge",
- "ProjectBadgeManager",
-]
-
-
-class GroupBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/badges"
- _obj_cls = GroupBadge
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("link_url", "image_url"))
- _update_attrs = RequiredOptional(optional=("link_url", "image_url"))
-
-
-class ProjectBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/badges"
- _obj_cls = ProjectBadge
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("link_url", "image_url"))
- _update_attrs = RequiredOptional(optional=("link_url", "image_url"))
diff --git a/gitlab/v4/objects/boards.py b/gitlab/v4/objects/boards.py
deleted file mode 100644
index 8b2959d..0000000
--- a/gitlab/v4/objects/boards.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupBoardList",
- "GroupBoardListManager",
- "GroupBoard",
- "GroupBoardManager",
- "ProjectBoardList",
- "ProjectBoardListManager",
- "ProjectBoard",
- "ProjectBoardManager",
-]
-
-
-class GroupBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupBoardListManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/boards/%(board_id)s/lists"
- _obj_cls = GroupBoardList
- _from_parent_attrs = {"group_id": "group_id", "board_id": "id"}
- _create_attrs = RequiredOptional(required=("label_id",))
- _update_attrs = RequiredOptional(required=("position",))
-
-
-class GroupBoard(SaveMixin, ObjectDeleteMixin, RESTObject):
- lists: GroupBoardListManager
-
-
-class GroupBoardManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/boards"
- _obj_cls = GroupBoard
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectBoardListManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/boards/%(board_id)s/lists"
- _obj_cls = ProjectBoardList
- _from_parent_attrs = {"project_id": "project_id", "board_id": "id"}
- _create_attrs = RequiredOptional(required=("label_id",))
- _update_attrs = RequiredOptional(required=("position",))
-
-
-class ProjectBoard(SaveMixin, ObjectDeleteMixin, RESTObject):
- lists: ProjectBoardListManager
-
-
-class ProjectBoardManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/boards"
- _obj_cls = ProjectBoard
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
diff --git a/gitlab/v4/objects/branches.py b/gitlab/v4/objects/branches.py
deleted file mode 100644
index 5bd8442..0000000
--- a/gitlab/v4/objects/branches.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectBranch",
- "ProjectBranchManager",
- "ProjectProtectedBranch",
- "ProjectProtectedBranchManager",
-]
-
-
-class ProjectBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectBranchManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/branches"
- _obj_cls = ProjectBranch
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("branch", "ref"))
-
-
-class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/protected_branches"
- _obj_cls = ProjectProtectedBranch
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",),
- optional=(
- "push_access_level",
- "merge_access_level",
- "unprotect_access_level",
- "allowed_to_push",
- "allowed_to_merge",
- "allowed_to_unprotect",
- "code_owner_approval_required",
- ),
- )
diff --git a/gitlab/v4/objects/broadcast_messages.py b/gitlab/v4/objects/broadcast_messages.py
deleted file mode 100644
index 7784997..0000000
--- a/gitlab/v4/objects/broadcast_messages.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "BroadcastMessage",
- "BroadcastMessageManager",
-]
-
-
-class BroadcastMessage(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class BroadcastMessageManager(CRUDMixin, RESTManager):
- _path = "/broadcast_messages"
- _obj_cls = BroadcastMessage
-
- _create_attrs = RequiredOptional(
- required=("message",), optional=("starts_at", "ends_at", "color", "font")
- )
- _update_attrs = RequiredOptional(
- optional=("message", "starts_at", "ends_at", "color", "font")
- )
diff --git a/gitlab/v4/objects/clusters.py b/gitlab/v4/objects/clusters.py
deleted file mode 100644
index 10ff202..0000000
--- a/gitlab/v4/objects/clusters.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupCluster",
- "GroupClusterManager",
- "ProjectCluster",
- "ProjectClusterManager",
-]
-
-
-class GroupCluster(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupClusterManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/clusters"
- _obj_cls = GroupCluster
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "platform_kubernetes_attributes"),
- optional=("domain", "enabled", "managed", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "domain",
- "management_project_id",
- "platform_kubernetes_attributes",
- "environment_scope",
- ),
- )
-
- @exc.on_http_error(exc.GitlabStopError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- path = "%s/user" % (self.path)
- return CreateMixin.create(self, data, path=path, **kwargs)
-
-
-class ProjectCluster(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectClusterManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/clusters"
- _obj_cls = ProjectCluster
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "platform_kubernetes_attributes"),
- optional=("domain", "enabled", "managed", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "domain",
- "management_project_id",
- "platform_kubernetes_attributes",
- "environment_scope",
- ),
- )
-
- @exc.on_http_error(exc.GitlabStopError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- path = "%s/user" % (self.path)
- return CreateMixin.create(self, data, path=path, **kwargs)
diff --git a/gitlab/v4/objects/commits.py b/gitlab/v4/objects/commits.py
deleted file mode 100644
index 05b55b0..0000000
--- a/gitlab/v4/objects/commits.py
+++ /dev/null
@@ -1,200 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, ListMixin, RefreshMixin, RetrieveMixin
-
-from .discussions import ProjectCommitDiscussionManager # noqa: F401
-
-__all__ = [
- "ProjectCommit",
- "ProjectCommitManager",
- "ProjectCommitComment",
- "ProjectCommitCommentManager",
- "ProjectCommitStatus",
- "ProjectCommitStatusManager",
-]
-
-
-class ProjectCommit(RESTObject):
- _short_print_attr = "title"
-
- comments: "ProjectCommitCommentManager"
- discussions: ProjectCommitDiscussionManager
- statuses: "ProjectCommitStatusManager"
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def diff(self, **kwargs):
- """Generate the commit diff.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the diff could not be retrieved
-
- Returns:
- list: The changes done in this commit
- """
- path = "%s/%s/diff" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", ("branch",))
- @exc.on_http_error(exc.GitlabCherryPickError)
- def cherry_pick(self, branch, **kwargs):
- """Cherry-pick a commit into a branch.
-
- Args:
- branch (str): Name of target branch
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCherryPickError: If the cherry-pick could not be performed
- """
- path = "%s/%s/cherry_pick" % (self.manager.path, self.get_id())
- post_data = {"branch": branch}
- self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", optional=("type",))
- @exc.on_http_error(exc.GitlabGetError)
- def refs(self, type="all", **kwargs):
- """List the references the commit is pushed to.
-
- Args:
- type (str): The scope of references ('branch', 'tag' or 'all')
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the references could not be retrieved
-
- Returns:
- list: The references the commit is pushed to.
- """
- path = "%s/%s/refs" % (self.manager.path, self.get_id())
- data = {"type": type}
- return self.manager.gitlab.http_get(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to the commit.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the references could not be retrieved
-
- Returns:
- list: The merge requests related to the commit.
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", ("branch",))
- @exc.on_http_error(exc.GitlabRevertError)
- def revert(self, branch, **kwargs):
- """Revert a commit on a given branch.
-
- Args:
- branch (str): Name of target branch
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRevertError: If the revert could not be performed
-
- Returns:
- dict: The new commit data (*not* a RESTObject)
- """
- path = "%s/%s/revert" % (self.manager.path, self.get_id())
- post_data = {"branch": branch}
- return self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def signature(self, **kwargs):
- """Get the signature of the commit.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the signature could not be retrieved
-
- Returns:
- dict: The commit's signature data
- """
- path = "%s/%s/signature" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class ProjectCommitManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits"
- _obj_cls = ProjectCommit
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("branch", "commit_message", "actions"),
- optional=("author_email", "author_name"),
- )
-
-
-class ProjectCommitComment(RESTObject):
- _id_attr = None
- _short_print_attr = "note"
-
-
-class ProjectCommitCommentManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/comments"
- _obj_cls = ProjectCommitComment
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(
- required=("note",), optional=("path", "line", "line_type")
- )
-
-
-class ProjectCommitStatus(RefreshMixin, RESTObject):
- pass
-
-
-class ProjectCommitStatusManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/statuses"
- _obj_cls = ProjectCommitStatus
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(
- required=("state",),
- optional=("description", "name", "context", "ref", "target_url", "coverage"),
- )
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- # project_id and commit_id are in the data dict when using the CLI, but
- # they are missing when using only the API
- # See #511
- base_path = "/projects/%(project_id)s/statuses/%(commit_id)s"
- if "project_id" in data and "commit_id" in data:
- path = base_path % data
- else:
- path = self._compute_path(base_path)
- return CreateMixin.create(self, data, path=path, **kwargs)
diff --git a/gitlab/v4/objects/container_registry.py b/gitlab/v4/objects/container_registry.py
deleted file mode 100644
index ce03d35..0000000
--- a/gitlab/v4/objects/container_registry.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin, RetrieveMixin
-
-__all__ = [
- "ProjectRegistryRepository",
- "ProjectRegistryRepositoryManager",
- "ProjectRegistryTag",
- "ProjectRegistryTagManager",
-]
-
-
-class ProjectRegistryRepository(ObjectDeleteMixin, RESTObject):
- tags: "ProjectRegistryTagManager"
-
-
-class ProjectRegistryRepositoryManager(DeleteMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/registry/repositories"
- _obj_cls = ProjectRegistryRepository
- _from_parent_attrs = {"project_id": "id"}
-
-
-class ProjectRegistryTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
- _obj_cls = ProjectRegistryTag
- _from_parent_attrs = {"project_id": "project_id", "repository_id": "id"}
- _path = "/projects/%(project_id)s/registry/repositories/%(repository_id)s/tags"
-
- @cli.register_custom_action(
- "ProjectRegistryTagManager",
- ("name_regex_delete",),
- optional=("keep_n", "name_regex_keep", "older_than"),
- )
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_in_bulk(self, name_regex_delete, **kwargs):
- """Delete Tag in bulk
-
- Args:
- name_regex_delete (string): The regex of the name to delete. To delete all
- tags specify .*.
- keep_n (integer): The amount of latest tags of given name to keep.
- name_regex_keep (string): The regex of the name to keep. This value
- overrides any matches from name_regex.
- older_than (string): Tags to delete that are older than the given time,
- written in human readable form 1h, 1d, 1month.
- **kwargs: Extra options to send to the server (e.g. sudo)
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- valid_attrs = ["keep_n", "name_regex_keep", "older_than"]
- data = {"name_regex_delete": name_regex_delete}
- data.update({k: v for k, v in kwargs.items() if k in valid_attrs})
- self.gitlab.http_delete(self.path, query_data=data, **kwargs)
diff --git a/gitlab/v4/objects/custom_attributes.py b/gitlab/v4/objects/custom_attributes.py
deleted file mode 100644
index 48296ca..0000000
--- a/gitlab/v4/objects/custom_attributes.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ObjectDeleteMixin, RetrieveMixin, SetMixin
-
-__all__ = [
- "GroupCustomAttribute",
- "GroupCustomAttributeManager",
- "ProjectCustomAttribute",
- "ProjectCustomAttributeManager",
- "UserCustomAttribute",
- "UserCustomAttributeManager",
-]
-
-
-class GroupCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/custom_attributes"
- _obj_cls = GroupCustomAttribute
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/custom_attributes"
- _obj_cls = ProjectCustomAttribute
- _from_parent_attrs = {"project_id": "id"}
-
-
-class UserCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class UserCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/custom_attributes"
- _obj_cls = UserCustomAttribute
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/deploy_keys.py b/gitlab/v4/objects/deploy_keys.py
deleted file mode 100644
index cf0507d..0000000
--- a/gitlab/v4/objects/deploy_keys.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "DeployKey",
- "DeployKeyManager",
- "ProjectKey",
- "ProjectKeyManager",
-]
-
-
-class DeployKey(RESTObject):
- pass
-
-
-class DeployKeyManager(ListMixin, RESTManager):
- _path = "/deploy_keys"
- _obj_cls = DeployKey
-
-
-class ProjectKey(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectKeyManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/deploy_keys"
- _obj_cls = ProjectKey
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("title", "key"), optional=("can_push",))
- _update_attrs = RequiredOptional(optional=("title", "can_push"))
-
- @cli.register_custom_action("ProjectKeyManager", ("key_id",))
- @exc.on_http_error(exc.GitlabProjectDeployKeyError)
- def enable(self, key_id, **kwargs):
- """Enable a deploy key for a project.
-
- Args:
- key_id (int): The ID of the key to enable
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabProjectDeployKeyError: If the key could not be enabled
- """
- path = "%s/%s/enable" % (self.path, key_id)
- self.gitlab.http_post(path, **kwargs)
diff --git a/gitlab/v4/objects/deploy_tokens.py b/gitlab/v4/objects/deploy_tokens.py
deleted file mode 100644
index c6ba0d6..0000000
--- a/gitlab/v4/objects/deploy_tokens.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "DeployToken",
- "DeployTokenManager",
- "GroupDeployToken",
- "GroupDeployTokenManager",
- "ProjectDeployToken",
- "ProjectDeployTokenManager",
-]
-
-
-class DeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class DeployTokenManager(ListMixin, RESTManager):
- _path = "/deploy_tokens"
- _obj_cls = DeployToken
-
-
-class GroupDeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/deploy_tokens"
- _from_parent_attrs = {"group_id": "id"}
- _obj_cls = GroupDeployToken
- _create_attrs = RequiredOptional(
- required=(
- "name",
- "scopes",
- ),
- optional=(
- "expires_at",
- "username",
- ),
- )
- _types = {"scopes": types.ListAttribute}
-
-
-class ProjectDeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/deploy_tokens"
- _from_parent_attrs = {"project_id": "id"}
- _obj_cls = ProjectDeployToken
- _create_attrs = RequiredOptional(
- required=(
- "name",
- "scopes",
- ),
- optional=(
- "expires_at",
- "username",
- ),
- )
- _types = {"scopes": types.ListAttribute}
diff --git a/gitlab/v4/objects/deployments.py b/gitlab/v4/objects/deployments.py
deleted file mode 100644
index 11c60d1..0000000
--- a/gitlab/v4/objects/deployments.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
-
-from .merge_requests import ProjectDeploymentMergeRequestManager # noqa: F401
-
-__all__ = [
- "ProjectDeployment",
- "ProjectDeploymentManager",
-]
-
-
-class ProjectDeployment(SaveMixin, RESTObject):
- mergerequests: ProjectDeploymentMergeRequestManager
-
-
-class ProjectDeploymentManager(RetrieveMixin, CreateMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/deployments"
- _obj_cls = ProjectDeployment
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "order_by",
- "sort",
- "updated_after",
- "updated_before",
- "environment",
- "status",
- )
- _create_attrs = RequiredOptional(
- required=("sha", "ref", "tag", "status", "environment")
- )
diff --git a/gitlab/v4/objects/discussions.py b/gitlab/v4/objects/discussions.py
deleted file mode 100644
index ae7a4d5..0000000
--- a/gitlab/v4/objects/discussions.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
-
-from .notes import ( # noqa: F401
- ProjectCommitDiscussionNoteManager,
- ProjectIssueDiscussionNoteManager,
- ProjectMergeRequestDiscussionNoteManager,
- ProjectSnippetDiscussionNoteManager,
-)
-
-__all__ = [
- "ProjectCommitDiscussion",
- "ProjectCommitDiscussionManager",
- "ProjectIssueDiscussion",
- "ProjectIssueDiscussionManager",
- "ProjectMergeRequestDiscussion",
- "ProjectMergeRequestDiscussionManager",
- "ProjectSnippetDiscussion",
- "ProjectSnippetDiscussionManager",
-]
-
-
-class ProjectCommitDiscussion(RESTObject):
- notes: ProjectCommitDiscussionNoteManager
-
-
-class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s/" "discussions"
- _obj_cls = ProjectCommitDiscussion
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
-
-
-class ProjectIssueDiscussion(RESTObject):
- notes: ProjectIssueDiscussionNoteManager
-
-
-class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/discussions"
- _obj_cls = ProjectIssueDiscussion
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
-
-
-class ProjectMergeRequestDiscussion(SaveMixin, RESTObject):
- notes: ProjectMergeRequestDiscussionNoteManager
-
-
-class ProjectMergeRequestDiscussionManager(
- RetrieveMixin, CreateMixin, UpdateMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/discussions"
- _obj_cls = ProjectMergeRequestDiscussion
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(
- required=("body",), optional=("created_at", "position")
- )
- _update_attrs = RequiredOptional(required=("resolved",))
-
-
-class ProjectSnippetDiscussion(RESTObject):
- notes: ProjectSnippetDiscussionNoteManager
-
-
-class ProjectSnippetDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/discussions"
- _obj_cls = ProjectSnippetDiscussion
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
diff --git a/gitlab/v4/objects/environments.py b/gitlab/v4/objects/environments.py
deleted file mode 100644
index e318da8..0000000
--- a/gitlab/v4/objects/environments.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectEnvironment",
- "ProjectEnvironmentManager",
-]
-
-
-class ProjectEnvironment(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("ProjectEnvironment")
- @exc.on_http_error(exc.GitlabStopError)
- def stop(self, **kwargs):
- """Stop the environment.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabStopError: If the operation failed
- """
- path = "%s/%s/stop" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path, **kwargs)
-
-
-class ProjectEnvironmentManager(
- RetrieveMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/environments"
- _obj_cls = ProjectEnvironment
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("name",), optional=("external_url",))
- _update_attrs = RequiredOptional(optional=("name", "external_url"))
diff --git a/gitlab/v4/objects/epics.py b/gitlab/v4/objects/epics.py
deleted file mode 100644
index 90dc6ad..0000000
--- a/gitlab/v4/objects/epics.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .events import GroupEpicResourceLabelEventManager # noqa: F401
-
-__all__ = [
- "GroupEpic",
- "GroupEpicManager",
- "GroupEpicIssue",
- "GroupEpicIssueManager",
-]
-
-
-class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = "iid"
-
- issues: "GroupEpicIssueManager"
- resourcelabelevents: GroupEpicResourceLabelEventManager
-
-
-class GroupEpicManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/epics"
- _obj_cls = GroupEpic
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("author_id", "labels", "order_by", "sort", "search")
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=("labels", "description", "start_date", "end_date"),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "labels", "description", "start_date", "end_date"),
- )
- _types = {"labels": types.ListAttribute}
-
-
-class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = "epic_issue_id"
-
- def save(self, **kwargs):
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- updated_data = self._get_updated_data()
- # Nothing to update. Server fails if sent an empty dict.
- if not updated_data:
- return
-
- # call the manager
- obj_id = self.get_id()
- self.manager.update(obj_id, updated_data, **kwargs)
-
-
-class GroupEpicIssueManager(
- ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/groups/%(group_id)s/epics/%(epic_iid)s/issues"
- _obj_cls = GroupEpicIssue
- _from_parent_attrs = {"group_id": "group_id", "epic_iid": "iid"}
- _create_attrs = RequiredOptional(required=("issue_id",))
- _update_attrs = RequiredOptional(optional=("move_before_id", "move_after_id"))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- CreateMixin._check_missing_create_attrs(self, data)
- path = "%s/%s" % (self.path, data.pop("issue_id"))
- server_data = self.gitlab.http_post(path, **kwargs)
- # The epic_issue_id attribute doesn't exist when creating the resource,
- # but is used everywhere elese. Let's create it to be consistent client
- # side
- server_data["epic_issue_id"] = server_data["id"]
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/events.py b/gitlab/v4/objects/events.py
deleted file mode 100644
index 8772e8d..0000000
--- a/gitlab/v4/objects/events.py
+++ /dev/null
@@ -1,130 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import ListMixin, RetrieveMixin
-
-__all__ = [
- "Event",
- "EventManager",
- "GroupEpicResourceLabelEvent",
- "GroupEpicResourceLabelEventManager",
- "ProjectEvent",
- "ProjectEventManager",
- "ProjectIssueResourceLabelEvent",
- "ProjectIssueResourceLabelEventManager",
- "ProjectIssueResourceMilestoneEvent",
- "ProjectIssueResourceMilestoneEventManager",
- "ProjectIssueResourceStateEvent",
- "ProjectIssueResourceStateEventManager",
- "ProjectMergeRequestResourceLabelEvent",
- "ProjectMergeRequestResourceLabelEventManager",
- "ProjectMergeRequestResourceMilestoneEvent",
- "ProjectMergeRequestResourceMilestoneEventManager",
- "ProjectMergeRequestResourceStateEvent",
- "ProjectMergeRequestResourceStateEventManager",
- "UserEvent",
- "UserEventManager",
-]
-
-
-class Event(RESTObject):
- _id_attr = None
- _short_print_attr = "target_title"
-
-
-class EventManager(ListMixin, RESTManager):
- _path = "/events"
- _obj_cls = Event
- _list_filters = ("action", "target_type", "before", "after", "sort")
-
-
-class GroupEpicResourceLabelEvent(RESTObject):
- pass
-
-
-class GroupEpicResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/epics/%(epic_id)s/resource_label_events"
- _obj_cls = GroupEpicResourceLabelEvent
- _from_parent_attrs = {"group_id": "group_id", "epic_id": "id"}
-
-
-class ProjectEvent(Event):
- pass
-
-
-class ProjectEventManager(EventManager):
- _path = "/projects/%(project_id)s/events"
- _obj_cls = ProjectEvent
- _from_parent_attrs = {"project_id": "id"}
-
-
-class ProjectIssueResourceLabelEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s" "/resource_label_events"
- _obj_cls = ProjectIssueResourceLabelEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectIssueResourceMilestoneEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceMilestoneEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/resource_milestone_events"
- _obj_cls = ProjectIssueResourceMilestoneEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectIssueResourceStateEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceStateEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/resource_state_events"
- _obj_cls = ProjectIssueResourceStateEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectMergeRequestResourceLabelEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s" "/resource_label_events"
- )
- _obj_cls = ProjectMergeRequestResourceLabelEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectMergeRequestResourceMilestoneEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceMilestoneEventManager(RetrieveMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s/resource_milestone_events"
- )
- _obj_cls = ProjectMergeRequestResourceMilestoneEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectMergeRequestResourceStateEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceStateEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/resource_state_events"
- _obj_cls = ProjectMergeRequestResourceStateEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class UserEvent(Event):
- pass
-
-
-class UserEventManager(EventManager):
- _path = "/users/%(user_id)s/events"
- _obj_cls = UserEvent
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/export_import.py b/gitlab/v4/objects/export_import.py
deleted file mode 100644
index ec4532a..0000000
--- a/gitlab/v4/objects/export_import.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DownloadMixin, GetWithoutIdMixin, RefreshMixin
-
-__all__ = [
- "GroupExport",
- "GroupExportManager",
- "GroupImport",
- "GroupImportManager",
- "ProjectExport",
- "ProjectExportManager",
- "ProjectImport",
- "ProjectImportManager",
-]
-
-
-class GroupExport(DownloadMixin, RESTObject):
- _id_attr = None
-
-
-class GroupExportManager(GetWithoutIdMixin, CreateMixin, RESTManager):
- _path = "/groups/%(group_id)s/export"
- _obj_cls = GroupExport
- _from_parent_attrs = {"group_id": "id"}
-
-
-class GroupImport(RESTObject):
- _id_attr = None
-
-
-class GroupImportManager(GetWithoutIdMixin, RESTManager):
- _path = "/groups/%(group_id)s/import"
- _obj_cls = GroupImport
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectExport(DownloadMixin, RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectExportManager(GetWithoutIdMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/export"
- _obj_cls = ProjectExport
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(optional=("description",))
-
-
-class ProjectImport(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectImportManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/import"
- _obj_cls = ProjectImport
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/features.py b/gitlab/v4/objects/features.py
deleted file mode 100644
index f4117c8..0000000
--- a/gitlab/v4/objects/features.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Feature",
- "FeatureManager",
-]
-
-
-class Feature(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class FeatureManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/features/"
- _obj_cls = Feature
-
- @exc.on_http_error(exc.GitlabSetError)
- def set(
- self,
- name,
- value,
- feature_group=None,
- user=None,
- group=None,
- project=None,
- **kwargs
- ):
- """Create or update the object.
-
- Args:
- name (str): The value to set for the object
- value (bool/int): The value to set for the object
- feature_group (str): A feature group name
- user (str): A GitLab username
- group (str): A GitLab group
- project (str): A GitLab project in form group/project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSetError: If an error occurred
-
- Returns:
- obj: The created/updated attribute
- """
- path = "%s/%s" % (self.path, name.replace("/", "%2F"))
- data = {
- "value": value,
- "feature_group": feature_group,
- "user": user,
- "group": group,
- "project": project,
- }
- data = utils.remove_none_from_dict(data)
- server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py
deleted file mode 100644
index ff45478..0000000
--- a/gitlab/v4/objects/files.py
+++ /dev/null
@@ -1,228 +0,0 @@
-import base64
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectFile",
- "ProjectFileManager",
-]
-
-
-class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "file_path"
- _short_print_attr = "file_path"
-
- def decode(self) -> bytes:
- """Returns the decoded content of the file.
-
- Returns:
- (bytes): the decoded content.
- """
- return base64.b64decode(self.content)
-
- def save(self, branch, commit_message, **kwargs):
- """Save the changes made to the file to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- branch (str): Branch in which the file will be updated
- commit_message (str): Message to send with the commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- self.branch = branch
- self.commit_message = commit_message
- self.file_path = self.file_path.replace("/", "%2F")
- super(ProjectFile, self).save(**kwargs)
-
- def delete(self, branch, commit_message, **kwargs):
- """Delete the file from the server.
-
- Args:
- branch (str): Branch from which the file will be removed
- commit_message (str): Commit message for the deletion
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- file_path = self.get_id().replace("/", "%2F")
- self.manager.delete(file_path, branch, commit_message, **kwargs)
-
-
-class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/files"
- _obj_cls = ProjectFile
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("file_path", "branch", "content", "commit_message"),
- optional=("encoding", "author_email", "author_name"),
- )
- _update_attrs = RequiredOptional(
- required=("file_path", "branch", "content", "commit_message"),
- optional=("encoding", "author_email", "author_name"),
- )
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- def get(self, file_path, ref, **kwargs):
- """Retrieve a single file.
-
- Args:
- file_path (str): Path of the file to retrieve
- ref (str): Name of the branch, tag or commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the file could not be retrieved
-
- Returns:
- object: The generated RESTObject
- """
- return GetMixin.get(self, file_path, ref=ref, **kwargs)
-
- @cli.register_custom_action(
- "ProjectFileManager",
- ("file_path", "branch", "content", "commit_message"),
- ("encoding", "author_email", "author_name"),
- )
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject: a new instance of the managed object class built with
- the data sent by the server
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
-
- self._check_missing_create_attrs(data)
- new_data = data.copy()
- file_path = new_data.pop("file_path").replace("/", "%2F")
- path = "%s/%s" % (self.path, file_path)
- server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
- return self._obj_cls(self, server_data)
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, file_path, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- file_path = file_path.replace("/", "%2F")
- data["file_path"] = file_path
- path = "%s/%s" % (self.path, file_path)
- self._check_missing_update_attrs(data)
- return self.gitlab.http_put(path, post_data=data, **kwargs)
-
- @cli.register_custom_action(
- "ProjectFileManager", ("file_path", "branch", "commit_message")
- )
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, file_path, branch, commit_message, **kwargs):
- """Delete a file on the server.
-
- Args:
- file_path (str): Path of the file to remove
- branch (str): Branch from which the file will be removed
- commit_message (str): Commit message for the deletion
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- path = "%s/%s" % (self.path, file_path.replace("/", "%2F"))
- data = {"branch": branch, "commit_message": commit_message}
- self.gitlab.http_delete(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- @exc.on_http_error(exc.GitlabGetError)
- def raw(
- self, file_path, ref, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return the content of a file for a commit.
-
- Args:
- ref (str): ID of the commit
- filepath (str): Path of the file to return
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the file could not be retrieved
-
- Returns:
- str: The file content
- """
- file_path = file_path.replace("/", "%2F").replace(".", "%2E")
- path = "%s/%s/raw" % (self.path, file_path)
- query_data = {"ref": ref}
- result = self.gitlab.http_get(
- path, query_data=query_data, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- @exc.on_http_error(exc.GitlabListError)
- def blame(self, file_path, ref, **kwargs):
- """Return the content of a file for a commit.
-
- Args:
- file_path (str): Path of the file to retrieve
- ref (str): Name of the branch, tag or commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- list(blame): a list of commits/lines matching the file
- """
- file_path = file_path.replace("/", "%2F").replace(".", "%2E")
- path = "%s/%s/blame" % (self.path, file_path)
- query_data = {"ref": ref}
- return self.gitlab.http_list(path, query_data, **kwargs)
diff --git a/gitlab/v4/objects/geo_nodes.py b/gitlab/v4/objects/geo_nodes.py
deleted file mode 100644
index 16fc783..0000000
--- a/gitlab/v4/objects/geo_nodes.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- DeleteMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "GeoNode",
- "GeoNodeManager",
-]
-
-
-class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("GeoNode")
- @exc.on_http_error(exc.GitlabRepairError)
- def repair(self, **kwargs):
- """Repair the OAuth authentication of the geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRepairError: If the server failed to perform the request
- """
- path = "/geo_nodes/%s/repair" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("GeoNode")
- @exc.on_http_error(exc.GitlabGetError)
- def status(self, **kwargs):
- """Get the status of the geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- dict: The status of the geo node
- """
- path = "/geo_nodes/%s/status" % self.get_id()
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/geo_nodes"
- _obj_cls = GeoNode
- _update_attrs = RequiredOptional(
- optional=("enabled", "url", "files_max_capacity", "repos_max_capacity"),
- )
-
- @cli.register_custom_action("GeoNodeManager")
- @exc.on_http_error(exc.GitlabGetError)
- def status(self, **kwargs):
- """Get the status of all the geo nodes.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The status of all the geo nodes
- """
- return self.gitlab.http_list("/geo_nodes/status", **kwargs)
-
- @cli.register_custom_action("GeoNodeManager")
- @exc.on_http_error(exc.GitlabGetError)
- def current_failures(self, **kwargs):
- """Get the list of failures on the current geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The list of failures
- """
- return self.gitlab.http_list("/geo_nodes/current/failures", **kwargs)
diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py
deleted file mode 100644
index b675a39..0000000
--- a/gitlab/v4/objects/groups.py
+++ /dev/null
@@ -1,334 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-from .access_requests import GroupAccessRequestManager # noqa: F401
-from .audit_events import GroupAuditEventManager # noqa: F401
-from .badges import GroupBadgeManager # noqa: F401
-from .boards import GroupBoardManager # noqa: F401
-from .clusters import GroupClusterManager # noqa: F401
-from .custom_attributes import GroupCustomAttributeManager # noqa: F401
-from .deploy_tokens import GroupDeployTokenManager # noqa: F401
-from .epics import GroupEpicManager # noqa: F401
-from .export_import import GroupExportManager, GroupImportManager # noqa: F401
-from .hooks import GroupHookManager # noqa: F401
-from .issues import GroupIssueManager # noqa: F401
-from .labels import GroupLabelManager # noqa: F401
-from .members import ( # noqa: F401
- GroupBillableMemberManager,
- GroupMemberAllManager,
- GroupMemberManager,
-)
-from .merge_requests import GroupMergeRequestManager # noqa: F401
-from .milestones import GroupMilestoneManager # noqa: F401
-from .notification_settings import GroupNotificationSettingsManager # noqa: F401
-from .packages import GroupPackageManager # noqa: F401
-from .projects import GroupProjectManager # noqa: F401
-from .runners import GroupRunnerManager # noqa: F401
-from .statistics import GroupIssuesStatisticsManager # noqa: F401
-from .variables import GroupVariableManager # noqa: F401
-from .wikis import GroupWikiManager # noqa: F401
-
-__all__ = [
- "Group",
- "GroupManager",
- "GroupDescendantGroup",
- "GroupDescendantGroupManager",
- "GroupSubgroup",
- "GroupSubgroupManager",
-]
-
-
-class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "name"
-
- accessrequests: GroupAccessRequestManager
- audit_events: GroupAuditEventManager
- badges: GroupBadgeManager
- billable_members: GroupBillableMemberManager
- boards: GroupBoardManager
- clusters: GroupClusterManager
- customattributes: GroupCustomAttributeManager
- deploytokens: GroupDeployTokenManager
- descendant_groups: "GroupDescendantGroupManager"
- epics: GroupEpicManager
- exports: GroupExportManager
- hooks: GroupHookManager
- imports: GroupImportManager
- issues: GroupIssueManager
- issues_statistics: GroupIssuesStatisticsManager
- labels: GroupLabelManager
- members: GroupMemberManager
- members_all: GroupMemberAllManager
- mergerequests: GroupMergeRequestManager
- milestones: GroupMilestoneManager
- notificationsettings: GroupNotificationSettingsManager
- packages: GroupPackageManager
- projects: GroupProjectManager
- runners: GroupRunnerManager
- subgroups: "GroupSubgroupManager"
- variables: GroupVariableManager
- wikis: GroupWikiManager
-
- @cli.register_custom_action("Group", ("project_id",))
- @exc.on_http_error(exc.GitlabTransferProjectError)
- def transfer_project(self, project_id, **kwargs):
- """Transfer a project to this group.
-
- Args:
- to_project_id (int): ID of the project to transfer
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTransferProjectError: If the project could not be transferred
- """
- path = "/groups/%s/projects/%s" % (self.id, project_id)
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Group", ("scope", "search"))
- @exc.on_http_error(exc.GitlabSearchError)
- def search(self, scope, search, **kwargs):
- """Search the group resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- path = "/groups/%s/search" % self.get_id()
- return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("cn", "group_access", "provider"))
- @exc.on_http_error(exc.GitlabCreateError)
- def add_ldap_group_link(self, cn, group_access, provider, **kwargs):
- """Add an LDAP group link.
-
- Args:
- cn (str): CN of the LDAP group
- group_access (int): Minimum access level for members of the LDAP
- group
- provider (str): LDAP provider for the LDAP group
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_group_links" % self.get_id()
- data = {"cn": cn, "group_access": group_access, "provider": provider}
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("cn",), ("provider",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_ldap_group_link(self, cn, provider=None, **kwargs):
- """Delete an LDAP group link.
-
- Args:
- cn (str): CN of the LDAP group
- provider (str): LDAP provider for the LDAP group
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_group_links" % self.get_id()
- if provider is not None:
- path += "/%s" % provider
- path += "/%s" % cn
- self.manager.gitlab.http_delete(path)
-
- @cli.register_custom_action("Group")
- @exc.on_http_error(exc.GitlabCreateError)
- def ldap_sync(self, **kwargs):
- """Sync LDAP groups.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_sync" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Group", ("group_id", "group_access"), ("expires_at",))
- @exc.on_http_error(exc.GitlabCreateError)
- def share(self, group_id, group_access, expires_at=None, **kwargs):
- """Share the group with a group.
-
- Args:
- group_id (int): ID of the group.
- group_access (int): Access level for the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/groups/%s/share" % self.get_id()
- data = {
- "group_id": group_id,
- "group_access": group_access,
- "expires_at": expires_at,
- }
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("group_id",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def unshare(self, group_id, **kwargs):
- """Delete a shared group link within a group.
-
- Args:
- group_id (int): ID of the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/groups/%s/share/%s" % (self.get_id(), group_id)
- self.manager.gitlab.http_delete(path, **kwargs)
-
-
-class GroupManager(CRUDMixin, RESTManager):
- _path = "/groups"
- _obj_cls = Group
- _list_filters = (
- "skip_groups",
- "all_available",
- "search",
- "order_by",
- "sort",
- "statistics",
- "owned",
- "with_custom_attributes",
- "min_access_level",
- "top_level_only",
- )
- _create_attrs = RequiredOptional(
- required=("name", "path"),
- optional=(
- "description",
- "membership_lock",
- "visibility",
- "share_with_group_lock",
- "require_two_factor_authentication",
- "two_factor_grace_period",
- "project_creation_level",
- "auto_devops_enabled",
- "subgroup_creation_level",
- "emails_disabled",
- "avatar",
- "mentions_disabled",
- "lfs_enabled",
- "request_access_enabled",
- "parent_id",
- "default_branch_protection",
- "shared_runners_minutes_limit",
- "extra_shared_runners_minutes_limit",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "path",
- "description",
- "membership_lock",
- "share_with_group_lock",
- "visibility",
- "require_two_factor_authentication",
- "two_factor_grace_period",
- "project_creation_level",
- "auto_devops_enabled",
- "subgroup_creation_level",
- "emails_disabled",
- "avatar",
- "mentions_disabled",
- "lfs_enabled",
- "request_access_enabled",
- "default_branch_protection",
- "file_template_project_id",
- "shared_runners_minutes_limit",
- "extra_shared_runners_minutes_limit",
- "prevent_forking_outside_group",
- "shared_runners_setting",
- ),
- )
- _types = {"avatar": types.ImageAttribute, "skip_groups": types.ListAttribute}
-
- @exc.on_http_error(exc.GitlabImportError)
- def import_group(self, file, path, name, parent_id=None, **kwargs):
- """Import a group from an archive file.
-
- Args:
- file: Data or file object containing the group
- path (str): The path for the new group to be imported.
- name (str): The name for the new group.
- parent_id (str): ID of a parent group that the group will
- be imported into.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabImportError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
- """
- files = {"file": ("file.tar.gz", file, "application/octet-stream")}
- data = {"path": path, "name": name}
- if parent_id is not None:
- data["parent_id"] = parent_id
-
- return self.gitlab.http_post(
- "/groups/import", post_data=data, files=files, **kwargs
- )
-
-
-class GroupSubgroup(RESTObject):
- pass
-
-
-class GroupSubgroupManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/subgroups"
- _obj_cls = GroupSubgroup
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "skip_groups",
- "all_available",
- "search",
- "order_by",
- "sort",
- "statistics",
- "owned",
- "with_custom_attributes",
- "min_access_level",
- )
- _types = {"skip_groups": types.ListAttribute}
-
-
-class GroupDescendantGroup(RESTObject):
- pass
-
-
-class GroupDescendantGroupManager(GroupSubgroupManager):
- """
- This manager inherits from GroupSubgroupManager as descendant groups
- share all attributes with subgroups, except the path and object class.
- """
-
- _path = "/groups/%(group_id)s/descendant_groups"
- _obj_cls = GroupDescendantGroup
diff --git a/gitlab/v4/objects/hooks.py b/gitlab/v4/objects/hooks.py
deleted file mode 100644
index 428fd76..0000000
--- a/gitlab/v4/objects/hooks.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, NoUpdateMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "Hook",
- "HookManager",
- "ProjectHook",
- "ProjectHookManager",
- "GroupHook",
- "GroupHookManager",
-]
-
-
-class Hook(ObjectDeleteMixin, RESTObject):
- _url = "/hooks"
- _short_print_attr = "url"
-
-
-class HookManager(NoUpdateMixin, RESTManager):
- _path = "/hooks"
- _obj_cls = Hook
- _create_attrs = RequiredOptional(required=("url",))
-
-
-class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "url"
-
-
-class ProjectHookManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/hooks"
- _obj_cls = ProjectHook
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "enable_ssl_verification",
- "token",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "job_events",
- "pipeline_events",
- "wiki_events",
- "enable_ssl_verification",
- "token",
- ),
- )
-
-
-class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "url"
-
-
-class GroupHookManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/hooks"
- _obj_cls = GroupHook
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "deployment_events",
- "releases_events",
- "subgroup_events",
- "enable_ssl_verification",
- "token",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "deployment_events",
- "releases_events",
- "subgroup_events",
- "enable_ssl_verification",
- "token",
- ),
- )
diff --git a/gitlab/v4/objects/issues.py b/gitlab/v4/objects/issues.py
deleted file mode 100644
index 9272908..0000000
--- a/gitlab/v4/objects/issues.py
+++ /dev/null
@@ -1,256 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- ParticipantsMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- TimeTrackingMixin,
- TodoMixin,
- UserAgentDetailMixin,
-)
-
-from .award_emojis import ProjectIssueAwardEmojiManager # noqa: F401
-from .discussions import ProjectIssueDiscussionManager # noqa: F401
-from .events import ( # noqa: F401
- ProjectIssueResourceLabelEventManager,
- ProjectIssueResourceMilestoneEventManager,
- ProjectIssueResourceStateEventManager,
-)
-from .notes import ProjectIssueNoteManager # noqa: F401
-
-__all__ = [
- "Issue",
- "IssueManager",
- "GroupIssue",
- "GroupIssueManager",
- "ProjectIssue",
- "ProjectIssueManager",
- "ProjectIssueLink",
- "ProjectIssueLinkManager",
-]
-
-
-class Issue(RESTObject):
- _url = "/issues"
- _short_print_attr = "title"
-
-
-class IssueManager(RetrieveMixin, RESTManager):
- _path = "/issues"
- _obj_cls = Issue
- _list_filters = (
- "state",
- "labels",
- "milestone",
- "scope",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "iids",
- "order_by",
- "sort",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class GroupIssue(RESTObject):
- pass
-
-
-class GroupIssueManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/issues"
- _obj_cls = GroupIssue
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "state",
- "labels",
- "milestone",
- "order_by",
- "sort",
- "iids",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class ProjectIssue(
- UserAgentDetailMixin,
- SubscribableMixin,
- TodoMixin,
- TimeTrackingMixin,
- ParticipantsMixin,
- SaveMixin,
- ObjectDeleteMixin,
- RESTObject,
-):
- _short_print_attr = "title"
- _id_attr = "iid"
-
- awardemojis: ProjectIssueAwardEmojiManager
- discussions: ProjectIssueDiscussionManager
- links: "ProjectIssueLinkManager"
- notes: ProjectIssueNoteManager
- resourcelabelevents: ProjectIssueResourceLabelEventManager
- resourcemilestoneevents: ProjectIssueResourceMilestoneEventManager
- resourcestateevents: ProjectIssueResourceStateEventManager
-
- @cli.register_custom_action("ProjectIssue", ("to_project_id",))
- @exc.on_http_error(exc.GitlabUpdateError)
- def move(self, to_project_id, **kwargs):
- """Move the issue to another project.
-
- Args:
- to_project_id(int): ID of the target project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the issue could not be moved
- """
- path = "%s/%s/move" % (self.manager.path, self.get_id())
- data = {"to_project_id": to_project_id}
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectIssue")
- @exc.on_http_error(exc.GitlabGetError)
- def related_merge_requests(self, **kwargs):
- """List merge requests related to the issue.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetErrot: If the merge requests could not be retrieved
-
- Returns:
- list: The list of merge requests.
- """
- path = "%s/%s/related_merge_requests" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectIssue")
- @exc.on_http_error(exc.GitlabGetError)
- def closed_by(self, **kwargs):
- """List merge requests that will close the issue when merged.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetErrot: If the merge requests could not be retrieved
-
- Returns:
- list: The list of merge requests.
- """
- path = "%s/%s/closed_by" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class ProjectIssueManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues"
- _obj_cls = ProjectIssue
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "iids",
- "state",
- "labels",
- "milestone",
- "scope",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "order_by",
- "sort",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=(
- "description",
- "confidential",
- "assignee_ids",
- "assignee_id",
- "milestone_id",
- "labels",
- "created_at",
- "due_date",
- "merge_request_to_resolve_discussions_of",
- "discussion_to_resolve",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "title",
- "description",
- "confidential",
- "assignee_ids",
- "assignee_id",
- "milestone_id",
- "labels",
- "state_event",
- "updated_at",
- "due_date",
- "discussion_locked",
- ),
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class ProjectIssueLink(ObjectDeleteMixin, RESTObject):
- _id_attr = "issue_link_id"
-
-
-class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/links"
- _obj_cls = ProjectIssueLink
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("target_project_id", "target_issue_iid"))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject, RESTObject: The source and target issues
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- self._check_missing_create_attrs(data)
- server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs)
- source_issue = ProjectIssue(self._parent.manager, server_data["source_issue"])
- target_issue = ProjectIssue(self._parent.manager, server_data["target_issue"])
- return source_issue, target_issue
diff --git a/gitlab/v4/objects/jobs.py b/gitlab/v4/objects/jobs.py
deleted file mode 100644
index 2e7693d..0000000
--- a/gitlab/v4/objects/jobs.py
+++ /dev/null
@@ -1,190 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RefreshMixin, RetrieveMixin
-
-__all__ = [
- "ProjectJob",
- "ProjectJobManager",
-]
-
-
-class ProjectJob(RefreshMixin, RESTObject):
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobCancelError)
- def cancel(self, **kwargs):
- """Cancel the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobCancelError: If the job could not be canceled
- """
- path = "%s/%s/cancel" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobRetryError)
- def retry(self, **kwargs):
- """Retry the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobRetryError: If the job could not be retried
- """
- path = "%s/%s/retry" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobPlayError)
- def play(self, **kwargs):
- """Trigger a job explicitly.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobPlayError: If the job could not be triggered
- """
- path = "%s/%s/play" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobEraseError)
- def erase(self, **kwargs):
- """Erase the job (remove job artifacts and trace).
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobEraseError: If the job could not be erased
- """
- path = "%s/%s/erase" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabCreateError)
- def keep_artifacts(self, **kwargs):
- """Prevent artifacts from being deleted when expiration is set.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the request could not be performed
- """
- path = "%s/%s/artifacts/keep" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabCreateError)
- def delete_artifacts(self, **kwargs):
- """Delete artifacts of a job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the request could not be performed
- """
- path = "%s/%s/artifacts" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_delete(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def artifacts(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get the job artifacts.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "%s/%s/artifacts" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def artifact(self, path, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get a single artifact file from within the job's artifacts archive.
-
- Args:
- path (str): Path of the artifact
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "%s/%s/artifacts/%s" % (self.manager.path, self.get_id(), path)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def trace(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get the job trace.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The trace
- """
- path = "%s/%s/trace" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectJobManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/jobs"
- _obj_cls = ProjectJob
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/keys.py b/gitlab/v4/objects/keys.py
deleted file mode 100644
index 7f8fa0e..0000000
--- a/gitlab/v4/objects/keys.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import GetMixin
-
-__all__ = [
- "Key",
- "KeyManager",
-]
-
-
-class Key(RESTObject):
- pass
-
-
-class KeyManager(GetMixin, RESTManager):
- _path = "/keys"
- _obj_cls = Key
-
- def get(self, id=None, **kwargs):
- if id is not None:
- return super(KeyManager, self).get(id, **kwargs)
-
- if "fingerprint" not in kwargs:
- raise AttributeError("Missing attribute: id or fingerprint")
-
- server_data = self.gitlab.http_get(self.path, **kwargs)
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/labels.py b/gitlab/v4/objects/labels.py
deleted file mode 100644
index 544c3cd..0000000
--- a/gitlab/v4/objects/labels.py
+++ /dev/null
@@ -1,149 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "GroupLabel",
- "GroupLabelManager",
- "ProjectLabel",
- "ProjectLabelManager",
-]
-
-
-class GroupLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
- # Update without ID, but we need an ID to get from list.
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Saves the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct.
- GitlabUpdateError: If the server cannot perform the request.
- """
- updated_data = self._get_updated_data()
-
- # call the manager
- server_data = self.manager.update(None, updated_data, **kwargs)
- self._update_attrs(server_data)
-
-
-class GroupLabelManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/labels"
- _obj_cls = GroupLabel
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "color"), optional=("description", "priority")
- )
- _update_attrs = RequiredOptional(
- required=("name",), optional=("new_name", "color", "description", "priority")
- )
-
- # Update without ID.
- def update(self, name, new_data=None, **kwargs):
- """Update a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
- """
- new_data = new_data or {}
- if name:
- new_data["name"] = name
- return super().update(id=None, new_data=new_data, **kwargs)
-
- # Delete without ID.
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, name, **kwargs):
- """Delete a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
-
-
-class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
- # Update without ID, but we need an ID to get from list.
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Saves the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct.
- GitlabUpdateError: If the server cannot perform the request.
- """
- updated_data = self._get_updated_data()
-
- # call the manager
- server_data = self.manager.update(None, updated_data, **kwargs)
- self._update_attrs(server_data)
-
-
-class ProjectLabelManager(
- RetrieveMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/labels"
- _obj_cls = ProjectLabel
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "color"), optional=("description", "priority")
- )
- _update_attrs = RequiredOptional(
- required=("name",), optional=("new_name", "color", "description", "priority")
- )
-
- # Update without ID.
- def update(self, name, new_data=None, **kwargs):
- """Update a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
- """
- new_data = new_data or {}
- if name:
- new_data["name"] = name
- return super().update(id=None, new_data=new_data, **kwargs)
-
- # Delete without ID.
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, name, **kwargs):
- """Delete a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
diff --git a/gitlab/v4/objects/ldap.py b/gitlab/v4/objects/ldap.py
deleted file mode 100644
index e0202a1..0000000
--- a/gitlab/v4/objects/ldap.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject, RESTObjectList
-
-__all__ = [
- "LDAPGroup",
- "LDAPGroupManager",
-]
-
-
-class LDAPGroup(RESTObject):
- _id_attr = None
-
-
-class LDAPGroupManager(RESTManager):
- _path = "/ldap/groups"
- _obj_cls = LDAPGroup
- _list_filters = ("search", "provider")
-
- @exc.on_http_error(exc.GitlabListError)
- def list(self, **kwargs):
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
- data = kwargs.copy()
- if self.gitlab.per_page:
- data.setdefault("per_page", self.gitlab.per_page)
-
- if "provider" in data:
- path = "/ldap/%s/groups" % data["provider"]
- else:
- path = self._path
-
- obj = self.gitlab.http_list(path, **data)
- if isinstance(obj, list):
- return [self._obj_cls(self, item) for item in obj]
- else:
- return RESTObjectList(self, self._obj_cls, obj)
diff --git a/gitlab/v4/objects/members.py b/gitlab/v4/objects/members.py
deleted file mode 100644
index 0c92185..0000000
--- a/gitlab/v4/objects/members.py
+++ /dev/null
@@ -1,92 +0,0 @@
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
-)
-
-__all__ = [
- "GroupBillableMember",
- "GroupBillableMemberManager",
- "GroupBillableMemberMembership",
- "GroupBillableMemberMembershipManager",
- "GroupMember",
- "GroupMemberManager",
- "GroupMemberAllManager",
- "ProjectMember",
- "ProjectMemberManager",
- "ProjectMemberAllManager",
-]
-
-
-class GroupMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
-
-class GroupMemberManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/members"
- _obj_cls = GroupMember
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("access_level", "user_id"), optional=("expires_at",)
- )
- _update_attrs = RequiredOptional(
- required=("access_level",), optional=("expires_at",)
- )
- _types = {"user_ids": types.ListAttribute}
-
-
-class GroupBillableMember(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
- memberships: "GroupBillableMemberMembershipManager"
-
-
-class GroupBillableMemberManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/billable_members"
- _obj_cls = GroupBillableMember
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("search", "sort")
-
-
-class GroupBillableMemberMembership(RESTObject):
- _id_attr = "user_id"
-
-
-class GroupBillableMemberMembershipManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/billable_members/%(user_id)s/memberships"
- _obj_cls = GroupBillableMemberMembership
- _from_parent_attrs = {"group_id": "group_id", "user_id": "id"}
-
-
-class GroupMemberAllManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/members/all"
- _obj_cls = GroupMember
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
-
-class ProjectMemberManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/members"
- _obj_cls = ProjectMember
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("access_level", "user_id"), optional=("expires_at",)
- )
- _update_attrs = RequiredOptional(
- required=("access_level",), optional=("expires_at",)
- )
- _types = {"user_ids": types.ListAttribute}
-
-
-class ProjectMemberAllManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/members/all"
- _obj_cls = ProjectMember
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/merge_request_approvals.py b/gitlab/v4/objects/merge_request_approvals.py
deleted file mode 100644
index 4a41ca4..0000000
--- a/gitlab/v4/objects/merge_request_approvals.py
+++ /dev/null
@@ -1,206 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectApproval",
- "ProjectApprovalManager",
- "ProjectApprovalRule",
- "ProjectApprovalRuleManager",
- "ProjectMergeRequestApproval",
- "ProjectMergeRequestApprovalManager",
- "ProjectMergeRequestApprovalRule",
- "ProjectMergeRequestApprovalRuleManager",
-]
-
-
-class ProjectApproval(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/approvals"
- _obj_cls = ProjectApproval
- _from_parent_attrs = {"project_id": "id"}
- _update_attrs = RequiredOptional(
- optional=(
- "approvals_before_merge",
- "reset_approvals_on_push",
- "disable_overriding_approvers_per_merge_request",
- "merge_requests_author_approval",
- "merge_requests_disable_committers_approval",
- ),
- )
- _update_uses_post = True
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def set_approvers(self, approver_ids=None, approver_group_ids=None, **kwargs):
- """Change project-level allowed approvers and approver groups.
-
- Args:
- approver_ids (list): User IDs that can approve MRs
- approver_group_ids (list): Group IDs whose members can approve MRs
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server failed to perform the request
- """
- approver_ids = approver_ids or []
- approver_group_ids = approver_group_ids or []
-
- path = "/projects/%s/approvers" % self._parent.get_id()
- data = {"approver_ids": approver_ids, "approver_group_ids": approver_group_ids}
- self.gitlab.http_put(path, post_data=data, **kwargs)
-
-
-class ProjectApprovalRule(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "id"
-
-
-class ProjectApprovalRuleManager(
- ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/approval_rules"
- _obj_cls = ProjectApprovalRule
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "approvals_required"),
- optional=("user_ids", "group_ids", "protected_branch_ids"),
- )
-
-
-class ProjectMergeRequestApproval(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/approvals"
- _obj_cls = ProjectMergeRequestApproval
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _update_attrs = RequiredOptional(required=("approvals_required",))
- _update_uses_post = True
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def set_approvers(
- self,
- approvals_required,
- approver_ids=None,
- approver_group_ids=None,
- approval_rule_name="name",
- **kwargs
- ):
- """Change MR-level allowed approvers and approver groups.
-
- Args:
- approvals_required (integer): The number of required approvals for this rule
- approver_ids (list of integers): User IDs that can approve MRs
- approver_group_ids (list): Group IDs whose members can approve MRs
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server failed to perform the request
- """
- approver_ids = approver_ids or []
- approver_group_ids = approver_group_ids or []
-
- data = {
- "name": approval_rule_name,
- "approvals_required": approvals_required,
- "rule_type": "regular",
- "user_ids": approver_ids,
- "group_ids": approver_group_ids,
- }
- approval_rules = self._parent.approval_rules
- """ update any existing approval rule matching the name"""
- existing_approval_rules = approval_rules.list()
- for ar in existing_approval_rules:
- if ar.name == approval_rule_name:
- ar.user_ids = data["user_ids"]
- ar.approvals_required = data["approvals_required"]
- ar.group_ids = data["group_ids"]
- ar.save()
- return ar
- """ if there was no rule matching the rule name, create a new one"""
- return approval_rules.create(data=data)
-
-
-class ProjectMergeRequestApprovalRule(SaveMixin, RESTObject):
- _id_attr = "approval_rule_id"
- _short_print_attr = "approval_rule"
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- # There is a mismatch between the name of our id attribute and the put REST API name for the
- # project_id, so we override it here.
- self.approval_rule_id = self.id
- self.merge_request_iid = self._parent_attrs["mr_iid"]
- self.id = self._parent_attrs["project_id"]
- # save will update self.id with the result from the server, so no need to overwrite with
- # what it was before we overwrote it."""
- SaveMixin.save(self, **kwargs)
-
-
-class ProjectMergeRequestApprovalRuleManager(
- ListMixin, UpdateMixin, CreateMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/approval_rules"
- _obj_cls = ProjectMergeRequestApprovalRule
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _list_filters = ("name", "rule_type")
- _update_attrs = RequiredOptional(
- required=(
- "id",
- "merge_request_iid",
- "approval_rule_id",
- "name",
- "approvals_required",
- ),
- optional=("user_ids", "group_ids"),
- )
- # Important: When approval_project_rule_id is set, the name, users and groups of
- # project-level rule will be copied. The approvals_required specified will be used. """
- _create_attrs = RequiredOptional(
- required=("id", "merge_request_iid", "name", "approvals_required"),
- optional=("approval_project_rule_id", "user_ids", "group_ids"),
- )
-
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- new_data = data.copy()
- new_data["id"] = self._from_parent_attrs["project_id"]
- new_data["merge_request_iid"] = self._from_parent_attrs["mr_iid"]
- return CreateMixin.create(self, new_data, **kwargs)
diff --git a/gitlab/v4/objects/merge_requests.py b/gitlab/v4/objects/merge_requests.py
deleted file mode 100644
index 4def98c..0000000
--- a/gitlab/v4/objects/merge_requests.py
+++ /dev/null
@@ -1,439 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import (
- CRUDMixin,
- ListMixin,
- ObjectDeleteMixin,
- ParticipantsMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- TimeTrackingMixin,
- TodoMixin,
-)
-
-from .award_emojis import ProjectMergeRequestAwardEmojiManager # noqa: F401
-from .commits import ProjectCommit, ProjectCommitManager
-from .discussions import ProjectMergeRequestDiscussionManager # noqa: F401
-from .events import ( # noqa: F401
- ProjectMergeRequestResourceLabelEventManager,
- ProjectMergeRequestResourceMilestoneEventManager,
- ProjectMergeRequestResourceStateEventManager,
-)
-from .issues import ProjectIssue, ProjectIssueManager
-from .merge_request_approvals import ( # noqa: F401
- ProjectMergeRequestApprovalManager,
- ProjectMergeRequestApprovalRuleManager,
-)
-from .notes import ProjectMergeRequestNoteManager # noqa: F401
-from .pipelines import ProjectMergeRequestPipelineManager # noqa: F401
-
-__all__ = [
- "MergeRequest",
- "MergeRequestManager",
- "GroupMergeRequest",
- "GroupMergeRequestManager",
- "ProjectMergeRequest",
- "ProjectMergeRequestManager",
- "ProjectDeploymentMergeRequest",
- "ProjectDeploymentMergeRequestManager",
- "ProjectMergeRequestDiff",
- "ProjectMergeRequestDiffManager",
-]
-
-
-class MergeRequest(RESTObject):
- pass
-
-
-class MergeRequestManager(ListMixin, RESTManager):
- _path = "/merge_requests"
- _obj_cls = MergeRequest
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "with_labels_details",
- "with_merge_status_recheck",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "author_id",
- "author_username",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "reviewer_id",
- "reviewer_username",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "in",
- "wip",
- "not",
- "environment",
- "deployed_before",
- "deployed_after",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "in": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class GroupMergeRequest(RESTObject):
- pass
-
-
-class GroupMergeRequestManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/merge_requests"
- _obj_cls = GroupMergeRequest
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "author_id",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "wip",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class ProjectMergeRequest(
- SubscribableMixin,
- TodoMixin,
- TimeTrackingMixin,
- ParticipantsMixin,
- SaveMixin,
- ObjectDeleteMixin,
- RESTObject,
-):
- _id_attr = "iid"
-
- approval_rules: ProjectMergeRequestApprovalRuleManager
- approvals: ProjectMergeRequestApprovalManager
- awardemojis: ProjectMergeRequestAwardEmojiManager
- diffs: "ProjectMergeRequestDiffManager"
- discussions: ProjectMergeRequestDiscussionManager
- notes: ProjectMergeRequestNoteManager
- pipelines: ProjectMergeRequestPipelineManager
- resourcelabelevents: ProjectMergeRequestResourceLabelEventManager
- resourcemilestoneevents: ProjectMergeRequestResourceMilestoneEventManager
- resourcestateevents: ProjectMergeRequestResourceStateEventManager
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMROnBuildSuccessError)
- def cancel_merge_when_pipeline_succeeds(self, **kwargs):
- """Cancel merge when the pipeline succeeds.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMROnBuildSuccessError: If the server could not handle the
- request
- """
-
- path = "%s/%s/cancel_merge_when_pipeline_succeeds" % (
- self.manager.path,
- self.get_id(),
- )
- server_data = self.manager.gitlab.http_put(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def closes_issues(self, **kwargs):
- """List issues that will close on merge."
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: List of issues
- """
- path = "%s/%s/closes_issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
- return RESTObjectList(manager, ProjectIssue, data_list)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def commits(self, **kwargs):
- """List the merge request commits.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of commits
- """
-
- path = "%s/%s/commits" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectCommitManager(self.manager.gitlab, parent=self.manager._parent)
- return RESTObjectList(manager, ProjectCommit, data_list)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def changes(self, **kwargs):
- """List the merge request changes.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: List of changes
- """
- path = "%s/%s/changes" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectMergeRequest", tuple(), ("sha",))
- @exc.on_http_error(exc.GitlabMRApprovalError)
- def approve(self, sha=None, **kwargs):
- """Approve the merge request.
-
- Args:
- sha (str): Head SHA of MR
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRApprovalError: If the approval failed
- """
- path = "%s/%s/approve" % (self.manager.path, self.get_id())
- data = {}
- if sha:
- data["sha"] = sha
-
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMRApprovalError)
- def unapprove(self, **kwargs):
- """Unapprove the merge request.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRApprovalError: If the unapproval failed
- """
- path = "%s/%s/unapprove" % (self.manager.path, self.get_id())
- data = {}
-
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMRRebaseError)
- def rebase(self, **kwargs):
- """Attempt to rebase the source branch onto the target branch
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRRebaseError: If rebasing failed
- """
- path = "%s/%s/rebase" % (self.manager.path, self.get_id())
- data = {}
- return self.manager.gitlab.http_put(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabGetError)
- def merge_ref(self, **kwargs):
- """Attempt to merge changes between source and target branches into
- `refs/merge-requests/:iid/merge`.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabGetError: If cannot be merged
- """
- path = "%s/%s/merge_ref" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action(
- "ProjectMergeRequest",
- tuple(),
- (
- "merge_commit_message",
- "should_remove_source_branch",
- "merge_when_pipeline_succeeds",
- ),
- )
- @exc.on_http_error(exc.GitlabMRClosedError)
- def merge(
- self,
- merge_commit_message=None,
- should_remove_source_branch=False,
- merge_when_pipeline_succeeds=False,
- **kwargs
- ):
- """Accept the merge request.
-
- Args:
- merge_commit_message (bool): Commit message
- should_remove_source_branch (bool): If True, removes the source
- branch
- merge_when_pipeline_succeeds (bool): Wait for the build to succeed,
- then merge
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRClosedError: If the merge failed
- """
- path = "%s/%s/merge" % (self.manager.path, self.get_id())
- data = {}
- if merge_commit_message:
- data["merge_commit_message"] = merge_commit_message
- if should_remove_source_branch is not None:
- data["should_remove_source_branch"] = should_remove_source_branch
- if merge_when_pipeline_succeeds:
- data["merge_when_pipeline_succeeds"] = True
-
- server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
-
-class ProjectMergeRequestManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests"
- _obj_cls = ProjectMergeRequest
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("source_branch", "target_branch", "title"),
- optional=(
- "assignee_id",
- "description",
- "target_project_id",
- "labels",
- "milestone_id",
- "remove_source_branch",
- "allow_maintainer_to_push",
- "squash",
- "reviewer_ids",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "target_branch",
- "assignee_id",
- "title",
- "description",
- "state_event",
- "labels",
- "milestone_id",
- "remove_source_branch",
- "discussion_locked",
- "allow_maintainer_to_push",
- "squash",
- "reviewer_ids",
- ),
- )
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "iids",
- "author_id",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "wip",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "iids": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class ProjectDeploymentMergeRequest(MergeRequest):
- pass
-
-
-class ProjectDeploymentMergeRequestManager(MergeRequestManager):
- _path = "/projects/%(project_id)s/deployments/%(deployment_id)s/merge_requests"
- _obj_cls = ProjectDeploymentMergeRequest
- _from_parent_attrs = {"deployment_id": "id", "project_id": "project_id"}
-
-
-class ProjectMergeRequestDiff(RESTObject):
- pass
-
-
-class ProjectMergeRequestDiffManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/versions"
- _obj_cls = ProjectMergeRequestDiff
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
diff --git a/gitlab/v4/objects/milestones.py b/gitlab/v4/objects/milestones.py
deleted file mode 100644
index 0a53e1b..0000000
--- a/gitlab/v4/objects/milestones.py
+++ /dev/null
@@ -1,164 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-from .issues import GroupIssue, GroupIssueManager, ProjectIssue, ProjectIssueManager
-from .merge_requests import (
- GroupMergeRequest,
- ProjectMergeRequest,
- ProjectMergeRequestManager,
-)
-
-__all__ = [
- "GroupMilestone",
- "GroupMilestoneManager",
- "ProjectMilestone",
- "ProjectMilestoneManager",
-]
-
-
-class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("GroupMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def issues(self, **kwargs):
- """List issues related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of issues
- """
-
- path = "%s/%s/issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, GroupIssue, data_list)
-
- @cli.register_custom_action("GroupMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of merge requests
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, GroupMergeRequest, data_list)
-
-
-class GroupMilestoneManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/milestones"
- _obj_cls = GroupMilestone
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title",), optional=("description", "due_date", "start_date")
- )
- _update_attrs = RequiredOptional(
- optional=("title", "description", "due_date", "start_date", "state_event"),
- )
- _list_filters = ("iids", "state", "search")
- _types = {"iids": types.ListAttribute}
-
-
-class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("ProjectMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def issues(self, **kwargs):
- """List issues related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of issues
- """
-
- path = "%s/%s/issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, ProjectIssue, data_list)
-
- @cli.register_custom_action("ProjectMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of merge requests
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectMergeRequestManager(
- self.manager.gitlab, parent=self.manager._parent
- )
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, ProjectMergeRequest, data_list)
-
-
-class ProjectMilestoneManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/milestones"
- _obj_cls = ProjectMilestone
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=("description", "due_date", "start_date", "state_event"),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "description", "due_date", "start_date", "state_event"),
- )
- _list_filters = ("iids", "state", "search")
- _types = {"iids": types.ListAttribute}
diff --git a/gitlab/v4/objects/namespaces.py b/gitlab/v4/objects/namespaces.py
deleted file mode 100644
index deee281..0000000
--- a/gitlab/v4/objects/namespaces.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "Namespace",
- "NamespaceManager",
-]
-
-
-class Namespace(RESTObject):
- pass
-
-
-class NamespaceManager(RetrieveMixin, RESTManager):
- _path = "/namespaces"
- _obj_cls = Namespace
- _list_filters = ("search",)
diff --git a/gitlab/v4/objects/notes.py b/gitlab/v4/objects/notes.py
deleted file mode 100644
index cbd237e..0000000
--- a/gitlab/v4/objects/notes.py
+++ /dev/null
@@ -1,169 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .award_emojis import ( # noqa: F401
- ProjectIssueNoteAwardEmojiManager,
- ProjectMergeRequestNoteAwardEmojiManager,
- ProjectSnippetNoteAwardEmojiManager,
-)
-
-__all__ = [
- "ProjectNote",
- "ProjectNoteManager",
- "ProjectCommitDiscussionNote",
- "ProjectCommitDiscussionNoteManager",
- "ProjectIssueNote",
- "ProjectIssueNoteManager",
- "ProjectIssueDiscussionNote",
- "ProjectIssueDiscussionNoteManager",
- "ProjectMergeRequestNote",
- "ProjectMergeRequestNoteManager",
- "ProjectMergeRequestDiscussionNote",
- "ProjectMergeRequestDiscussionNoteManager",
- "ProjectSnippetNote",
- "ProjectSnippetNoteManager",
- "ProjectSnippetDiscussionNote",
- "ProjectSnippetDiscussionNoteManager",
-]
-
-
-class ProjectNote(RESTObject):
- pass
-
-
-class ProjectNoteManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/notes"
- _obj_cls = ProjectNote
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectCommitDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectCommitDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/repository/commits/%(commit_id)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectCommitDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "commit_id": "commit_id",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(
- required=("body",), optional=("created_at", "position")
- )
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectIssueNoteAwardEmojiManager
-
-
-class ProjectIssueNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/notes"
- _obj_cls = ProjectIssueNote
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectIssueDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/issues/%(issue_iid)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectIssueDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "issue_iid": "issue_iid",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectMergeRequestNoteAwardEmojiManager
-
-
-class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/notes"
- _obj_cls = ProjectMergeRequestNote
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectMergeRequestDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectMergeRequestDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "mr_iid": "mr_iid",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectMergeRequestNoteAwardEmojiManager
-
-
-class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/notes"
- _obj_cls = ProjectSnippetNote
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("body",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectSnippetDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/snippets/%(snippet_id)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectSnippetDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "snippet_id": "snippet_id",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
diff --git a/gitlab/v4/objects/notification_settings.py b/gitlab/v4/objects/notification_settings.py
deleted file mode 100644
index 3682ed0..0000000
--- a/gitlab/v4/objects/notification_settings.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "NotificationSettings",
- "NotificationSettingsManager",
- "GroupNotificationSettings",
- "GroupNotificationSettingsManager",
- "ProjectNotificationSettings",
- "ProjectNotificationSettingsManager",
-]
-
-
-class NotificationSettings(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class NotificationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/notification_settings"
- _obj_cls = NotificationSettings
-
- _update_attrs = RequiredOptional(
- optional=(
- "level",
- "notification_email",
- "new_note",
- "new_issue",
- "reopen_issue",
- "close_issue",
- "reassign_issue",
- "new_merge_request",
- "reopen_merge_request",
- "close_merge_request",
- "reassign_merge_request",
- "merge_merge_request",
- ),
- )
-
-
-class GroupNotificationSettings(NotificationSettings):
- pass
-
-
-class GroupNotificationSettingsManager(NotificationSettingsManager):
- _path = "/groups/%(group_id)s/notification_settings"
- _obj_cls = GroupNotificationSettings
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectNotificationSettings(NotificationSettings):
- pass
-
-
-class ProjectNotificationSettingsManager(NotificationSettingsManager):
- _path = "/projects/%(project_id)s/notification_settings"
- _obj_cls = ProjectNotificationSettings
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py
deleted file mode 100644
index e76a5c6..0000000
--- a/gitlab/v4/objects/packages.py
+++ /dev/null
@@ -1,168 +0,0 @@
-from pathlib import Path
-from typing import Any, Callable, Optional, TYPE_CHECKING, Union
-
-import requests
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, GetMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "GenericPackage",
- "GenericPackageManager",
- "GroupPackage",
- "GroupPackageManager",
- "ProjectPackage",
- "ProjectPackageManager",
- "ProjectPackageFile",
- "ProjectPackageFileManager",
-]
-
-
-class GenericPackage(RESTObject):
- _id_attr = "package_name"
-
-
-class GenericPackageManager(RESTManager):
- _path = "/projects/%(project_id)s/packages/generic"
- _obj_cls = GenericPackage
- _from_parent_attrs = {"project_id": "id"}
-
- @cli.register_custom_action(
- "GenericPackageManager",
- ("package_name", "package_version", "file_name", "path"),
- )
- @exc.on_http_error(exc.GitlabUploadError)
- def upload(
- self,
- package_name: str,
- package_version: str,
- file_name: str,
- path: Union[str, Path],
- **kwargs,
- ) -> GenericPackage:
- """Upload a file as a generic package.
-
- Args:
- package_name (str): The package name. Must follow generic package
- name regex rules
- package_version (str): The package version. Must follow semantic
- version regex rules
- file_name (str): The name of the file as uploaded in the registry
- path (str): The path to a local file to upload
-
- Raises:
- GitlabConnectionError: If the server cannot be reached
- GitlabUploadError: If the file upload fails
- GitlabUploadError: If ``filepath`` cannot be read
-
- Returns:
- GenericPackage: An object storing the metadata of the uploaded package.
- """
-
- try:
- with open(path, "rb") as f:
- file_data = f.read()
- except OSError:
- raise exc.GitlabUploadError(f"Failed to read package file {path}")
-
- url = f"{self._computed_path}/{package_name}/{package_version}/{file_name}"
- server_data = self.gitlab.http_put(url, post_data=file_data, raw=True, **kwargs)
-
- return self._obj_cls(
- self,
- attrs={
- "package_name": package_name,
- "package_version": package_version,
- "file_name": file_name,
- "path": path,
- "message": server_data["message"],
- },
- )
-
- @cli.register_custom_action(
- "GenericPackageManager",
- ("package_name", "package_version", "file_name"),
- )
- @exc.on_http_error(exc.GitlabGetError)
- def download(
- self,
- package_name: str,
- package_version: str,
- file_name: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any,
- ) -> Optional[bytes]:
- """Download a generic package.
-
- Args:
- package_name (str): The package name.
- package_version (str): The package version.
- file_name (str): The name of the file in the registry
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The package content if streamed is False, None otherwise
- """
- path = f"{self._computed_path}/{package_name}/{package_version}/{file_name}"
- result = self.gitlab.http_get(path, streamed=streamed, raw=True, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class GroupPackage(RESTObject):
- pass
-
-
-class GroupPackageManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/packages"
- _obj_cls = GroupPackage
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "exclude_subgroups",
- "order_by",
- "sort",
- "package_type",
- "package_name",
- )
-
-
-class ProjectPackage(ObjectDeleteMixin, RESTObject):
- package_files: "ProjectPackageFileManager"
-
-
-class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/packages"
- _obj_cls = ProjectPackage
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "order_by",
- "sort",
- "package_type",
- "package_name",
- )
-
-
-class ProjectPackageFile(RESTObject):
- pass
-
-
-class ProjectPackageFileManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/packages/%(package_id)s/package_files"
- _obj_cls = ProjectPackageFile
- _from_parent_attrs = {"project_id": "project_id", "package_id": "id"}
diff --git a/gitlab/v4/objects/pages.py b/gitlab/v4/objects/pages.py
deleted file mode 100644
index 709d9f0..0000000
--- a/gitlab/v4/objects/pages.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "PagesDomain",
- "PagesDomainManager",
- "ProjectPagesDomain",
- "ProjectPagesDomainManager",
-]
-
-
-class PagesDomain(RESTObject):
- _id_attr = "domain"
-
-
-class PagesDomainManager(ListMixin, RESTManager):
- _path = "/pages/domains"
- _obj_cls = PagesDomain
-
-
-class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "domain"
-
-
-class ProjectPagesDomainManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/pages/domains"
- _obj_cls = ProjectPagesDomain
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("domain",), optional=("certificate", "key")
- )
- _update_attrs = RequiredOptional(optional=("certificate", "key"))
diff --git a/gitlab/v4/objects/personal_access_tokens.py b/gitlab/v4/objects/personal_access_tokens.py
deleted file mode 100644
index 6cdb305..0000000
--- a/gitlab/v4/objects/personal_access_tokens.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "PersonalAccessToken",
- "PersonalAccessTokenManager",
- "UserPersonalAccessToken",
- "UserPersonalAccessTokenManager",
-]
-
-
-class PersonalAccessToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class PersonalAccessTokenManager(DeleteMixin, ListMixin, RESTManager):
- _path = "/personal_access_tokens"
- _obj_cls = PersonalAccessToken
- _list_filters = ("user_id",)
-
-
-class UserPersonalAccessToken(RESTObject):
- pass
-
-
-class UserPersonalAccessTokenManager(CreateMixin, RESTManager):
- _path = "/users/%(user_id)s/personal_access_tokens"
- _obj_cls = UserPersonalAccessToken
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "scopes"), optional=("expires_at",)
- )
diff --git a/gitlab/v4/objects/pipelines.py b/gitlab/v4/objects/pipelines.py
deleted file mode 100644
index 2d212a6..0000000
--- a/gitlab/v4/objects/pipelines.py
+++ /dev/null
@@ -1,227 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- ObjectDeleteMixin,
- RefreshMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectMergeRequestPipeline",
- "ProjectMergeRequestPipelineManager",
- "ProjectPipeline",
- "ProjectPipelineManager",
- "ProjectPipelineJob",
- "ProjectPipelineJobManager",
- "ProjectPipelineBridge",
- "ProjectPipelineBridgeManager",
- "ProjectPipelineVariable",
- "ProjectPipelineVariableManager",
- "ProjectPipelineScheduleVariable",
- "ProjectPipelineScheduleVariableManager",
- "ProjectPipelineSchedule",
- "ProjectPipelineScheduleManager",
- "ProjectPipelineTestReport",
- "ProjectPipelineTestReportManager",
-]
-
-
-class ProjectMergeRequestPipeline(RESTObject):
- pass
-
-
-class ProjectMergeRequestPipelineManager(CreateMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/pipelines"
- _obj_cls = ProjectMergeRequestPipeline
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject):
- bridges: "ProjectPipelineBridgeManager"
- jobs: "ProjectPipelineJobManager"
- test_report: "ProjectPipelineTestReportManager"
- variables: "ProjectPipelineVariableManager"
-
- @cli.register_custom_action("ProjectPipeline")
- @exc.on_http_error(exc.GitlabPipelineCancelError)
- def cancel(self, **kwargs):
- """Cancel the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelineCancelError: If the request failed
- """
- path = "%s/%s/cancel" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectPipeline")
- @exc.on_http_error(exc.GitlabPipelineRetryError)
- def retry(self, **kwargs):
- """Retry the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelineRetryError: If the request failed
- """
- path = "%s/%s/retry" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
-
-class ProjectPipelineManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines"
- _obj_cls = ProjectPipeline
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "scope",
- "status",
- "ref",
- "sha",
- "yaml_errors",
- "name",
- "username",
- "order_by",
- "sort",
- )
- _create_attrs = RequiredOptional(required=("ref",))
-
- def create(self, data, **kwargs):
- """Creates a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the managed object class build with
- the data sent by the server
- """
- path = self.path[:-1] # drop the 's'
- return CreateMixin.create(self, data, path=path, **kwargs)
-
-
-class ProjectPipelineJob(RESTObject):
- pass
-
-
-class ProjectPipelineJobManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs"
- _obj_cls = ProjectPipelineJob
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
- _list_filters = ("scope", "include_retried")
-
-
-class ProjectPipelineBridge(RESTObject):
- pass
-
-
-class ProjectPipelineBridgeManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/bridges"
- _obj_cls = ProjectPipelineBridge
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
- _list_filters = ("scope",)
-
-
-class ProjectPipelineVariable(RESTObject):
- _id_attr = "key"
-
-
-class ProjectPipelineVariableManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/variables"
- _obj_cls = ProjectPipelineVariable
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
-
-
-class ProjectPipelineScheduleVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class ProjectPipelineScheduleVariableManager(
- CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/pipeline_schedules/"
- "%(pipeline_schedule_id)s/variables"
- )
- _obj_cls = ProjectPipelineScheduleVariable
- _from_parent_attrs = {"project_id": "project_id", "pipeline_schedule_id": "id"}
- _create_attrs = RequiredOptional(required=("key", "value"))
- _update_attrs = RequiredOptional(required=("key", "value"))
-
-
-class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
- variables: ProjectPipelineScheduleVariableManager
-
- @cli.register_custom_action("ProjectPipelineSchedule")
- @exc.on_http_error(exc.GitlabOwnershipError)
- def take_ownership(self, **kwargs):
- """Update the owner of a pipeline schedule.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabOwnershipError: If the request failed
- """
- path = "%s/%s/take_ownership" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectPipelineSchedule")
- @exc.on_http_error(exc.GitlabPipelinePlayError)
- def play(self, **kwargs):
- """Trigger a new scheduled pipeline, which runs immediately.
- The next scheduled run of this pipeline is not affected.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelinePlayError: If the request failed
- """
- path = "%s/%s/play" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
- return server_data
-
-
-class ProjectPipelineScheduleManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipeline_schedules"
- _obj_cls = ProjectPipelineSchedule
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("description", "ref", "cron"), optional=("cron_timezone", "active")
- )
- _update_attrs = RequiredOptional(
- optional=("description", "ref", "cron", "cron_timezone", "active"),
- )
-
-
-class ProjectPipelineTestReport(RESTObject):
- _id_attr = None
-
-
-class ProjectPipelineTestReportManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/test_report"
- _obj_cls = ProjectPipelineTestReport
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
diff --git a/gitlab/v4/objects/project_access_tokens.py b/gitlab/v4/objects/project_access_tokens.py
deleted file mode 100644
index f59ea85..0000000
--- a/gitlab/v4/objects/project_access_tokens.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectAccessToken",
- "ProjectAccessTokenManager",
-]
-
-
-class ProjectAccessToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectAccessTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/access_tokens"
- _obj_cls = ProjectAccessToken
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py
deleted file mode 100644
index 551079a..0000000
--- a/gitlab/v4/objects/projects.py
+++ /dev/null
@@ -1,1047 +0,0 @@
-from typing import Any, Callable, cast, Dict, List, Optional, TYPE_CHECKING, Union
-
-import requests
-
-from gitlab import cli, client
-from gitlab import exceptions as exc
-from gitlab import types, utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- ListMixin,
- ObjectDeleteMixin,
- RefreshMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .access_requests import ProjectAccessRequestManager # noqa: F401
-from .audit_events import ProjectAuditEventManager # noqa: F401
-from .badges import ProjectBadgeManager # noqa: F401
-from .boards import ProjectBoardManager # noqa: F401
-from .branches import ProjectBranchManager, ProjectProtectedBranchManager # noqa: F401
-from .clusters import ProjectClusterManager # noqa: F401
-from .commits import ProjectCommitManager # noqa: F401
-from .container_registry import ProjectRegistryRepositoryManager # noqa: F401
-from .custom_attributes import ProjectCustomAttributeManager # noqa: F401
-from .deploy_keys import ProjectKeyManager # noqa: F401
-from .deploy_tokens import ProjectDeployTokenManager # noqa: F401
-from .deployments import ProjectDeploymentManager # noqa: F401
-from .environments import ProjectEnvironmentManager # noqa: F401
-from .events import ProjectEventManager # noqa: F401
-from .export_import import ProjectExportManager, ProjectImportManager # noqa: F401
-from .files import ProjectFileManager # noqa: F401
-from .hooks import ProjectHookManager # noqa: F401
-from .issues import ProjectIssueManager # noqa: F401
-from .jobs import ProjectJobManager # noqa: F401
-from .labels import ProjectLabelManager # noqa: F401
-from .members import ProjectMemberAllManager, ProjectMemberManager # noqa: F401
-from .merge_request_approvals import ( # noqa: F401
- ProjectApprovalManager,
- ProjectApprovalRuleManager,
-)
-from .merge_requests import ProjectMergeRequestManager # noqa: F401
-from .milestones import ProjectMilestoneManager # noqa: F401
-from .notes import ProjectNoteManager # noqa: F401
-from .notification_settings import ProjectNotificationSettingsManager # noqa: F401
-from .packages import GenericPackageManager, ProjectPackageManager # noqa: F401
-from .pages import ProjectPagesDomainManager # noqa: F401
-from .pipelines import ( # noqa: F401
- ProjectPipeline,
- ProjectPipelineManager,
- ProjectPipelineScheduleManager,
-)
-from .project_access_tokens import ProjectAccessTokenManager # noqa: F401
-from .push_rules import ProjectPushRulesManager # noqa: F401
-from .releases import ProjectReleaseManager # noqa: F401
-from .repositories import RepositoryMixin
-from .runners import ProjectRunnerManager # noqa: F401
-from .services import ProjectServiceManager # noqa: F401
-from .snippets import ProjectSnippetManager # noqa: F401
-from .statistics import ( # noqa: F401
- ProjectAdditionalStatisticsManager,
- ProjectIssuesStatisticsManager,
-)
-from .tags import ProjectProtectedTagManager, ProjectTagManager # noqa: F401
-from .triggers import ProjectTriggerManager # noqa: F401
-from .users import ProjectUserManager # noqa: F401
-from .variables import ProjectVariableManager # noqa: F401
-from .wikis import ProjectWikiManager # noqa: F401
-
-__all__ = [
- "GroupProject",
- "GroupProjectManager",
- "Project",
- "ProjectManager",
- "ProjectFork",
- "ProjectForkManager",
- "ProjectRemoteMirror",
- "ProjectRemoteMirrorManager",
-]
-
-
-class GroupProject(RESTObject):
- pass
-
-
-class GroupProjectManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/projects"
- _obj_cls = GroupProject
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "starred",
- "with_custom_attributes",
- "include_subgroups",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_shared",
- "min_access_level",
- "with_security_reports",
- )
-
-
-class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTObject):
- _short_print_attr = "path"
-
- access_tokens: ProjectAccessTokenManager
- accessrequests: ProjectAccessRequestManager
- additionalstatistics: ProjectAdditionalStatisticsManager
- approvalrules: ProjectApprovalRuleManager
- approvals: ProjectApprovalManager
- audit_events: ProjectAuditEventManager
- badges: ProjectBadgeManager
- boards: ProjectBoardManager
- branches: ProjectBranchManager
- clusters: ProjectClusterManager
- commits: ProjectCommitManager
- customattributes: ProjectCustomAttributeManager
- deployments: ProjectDeploymentManager
- deploytokens: ProjectDeployTokenManager
- environments: ProjectEnvironmentManager
- events: ProjectEventManager
- exports: ProjectExportManager
- files: ProjectFileManager
- forks: "ProjectForkManager"
- generic_packages: GenericPackageManager
- hooks: ProjectHookManager
- imports: ProjectImportManager
- issues: ProjectIssueManager
- issues_statistics: ProjectIssuesStatisticsManager
- jobs: ProjectJobManager
- keys: ProjectKeyManager
- labels: ProjectLabelManager
- members: ProjectMemberManager
- members_all: ProjectMemberAllManager
- mergerequests: ProjectMergeRequestManager
- milestones: ProjectMilestoneManager
- notes: ProjectNoteManager
- notificationsettings: ProjectNotificationSettingsManager
- packages: ProjectPackageManager
- pagesdomains: ProjectPagesDomainManager
- pipelines: ProjectPipelineManager
- pipelineschedules: ProjectPipelineScheduleManager
- protectedbranches: ProjectProtectedBranchManager
- protectedtags: ProjectProtectedTagManager
- pushrules: ProjectPushRulesManager
- releases: ProjectReleaseManager
- remote_mirrors: "ProjectRemoteMirrorManager"
- repositories: ProjectRegistryRepositoryManager
- runners: ProjectRunnerManager
- services: ProjectServiceManager
- snippets: ProjectSnippetManager
- tags: ProjectTagManager
- triggers: ProjectTriggerManager
- users: ProjectUserManager
- variables: ProjectVariableManager
- wikis: ProjectWikiManager
-
- @cli.register_custom_action("Project", ("forked_from_id",))
- @exc.on_http_error(exc.GitlabCreateError)
- def create_fork_relation(self, forked_from_id: int, **kwargs: Any) -> None:
- """Create a forked from/to relation between existing projects.
-
- Args:
- forked_from_id (int): The ID of the project that was forked from
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the relation could not be created
- """
- path = "/projects/%s/fork/%s" % (self.get_id(), forked_from_id)
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_fork_relation(self, **kwargs: Any) -> None:
- """Delete a forked relation between existing projects.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/fork" % self.get_id()
- self.manager.gitlab.http_delete(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabGetError)
- def languages(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Get languages used in the project with percentage value.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
- """
- path = "/projects/%s/languages" % self.get_id()
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def star(self, **kwargs: Any) -> None:
- """Star a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/star" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def unstar(self, **kwargs: Any) -> None:
- """Unstar a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/unstar" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def archive(self, **kwargs: Any) -> None:
- """Archive a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/archive" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def unarchive(self, **kwargs: Any) -> None:
- """Unarchive a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/unarchive" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action(
- "Project", ("group_id", "group_access"), ("expires_at",)
- )
- @exc.on_http_error(exc.GitlabCreateError)
- def share(
- self,
- group_id: int,
- group_access: int,
- expires_at: Optional[str] = None,
- **kwargs: Any
- ) -> None:
- """Share the project with a group.
-
- Args:
- group_id (int): ID of the group.
- group_access (int): Access level for the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/share" % self.get_id()
- data = {
- "group_id": group_id,
- "group_access": group_access,
- "expires_at": expires_at,
- }
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Project", ("group_id",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def unshare(self, group_id: int, **kwargs: Any) -> None:
- """Delete a shared project link within a group.
-
- Args:
- group_id (int): ID of the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/share/%s" % (self.get_id(), group_id)
- self.manager.gitlab.http_delete(path, **kwargs)
-
- # variables not supported in CLI
- @cli.register_custom_action("Project", ("ref", "token"))
- @exc.on_http_error(exc.GitlabCreateError)
- def trigger_pipeline(
- self,
- ref: str,
- token: str,
- variables: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> ProjectPipeline:
- """Trigger a CI build.
-
- See https://gitlab.com/help/ci/triggers/README.md#trigger-a-build
-
- Args:
- ref (str): Commit to build; can be a branch name or a tag
- token (str): The trigger token
- variables (dict): Variables passed to the build script
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- variables = variables or {}
- path = "/projects/%s/trigger/pipeline" % self.get_id()
- post_data = {"ref": ref, "token": token, "variables": variables}
- attrs = self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(attrs, dict)
- return ProjectPipeline(self.pipelines, attrs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabHousekeepingError)
- def housekeeping(self, **kwargs: Any) -> None:
- """Start the housekeeping task.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabHousekeepingError: If the server failed to perform the
- request
- """
- path = "/projects/%s/housekeeping" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- # see #56 - add file attachment features
- @cli.register_custom_action("Project", ("filename", "filepath"))
- @exc.on_http_error(exc.GitlabUploadError)
- def upload(
- self,
- filename: str,
- filedata: Optional[bytes] = None,
- filepath: Optional[str] = None,
- **kwargs: Any
- ) -> Dict[str, Any]:
- """Upload the specified file into the project.
-
- .. note::
-
- Either ``filedata`` or ``filepath`` *MUST* be specified.
-
- Args:
- filename (str): The name of the file being uploaded
- filedata (bytes): The raw data of the file being uploaded
- filepath (str): The path to a local file to upload (optional)
-
- Raises:
- GitlabConnectionError: If the server cannot be reached
- GitlabUploadError: If the file upload fails
- GitlabUploadError: If ``filedata`` and ``filepath`` are not
- specified
- GitlabUploadError: If both ``filedata`` and ``filepath`` are
- specified
-
- Returns:
- dict: A ``dict`` with the keys:
- * ``alt`` - The alternate text for the upload
- * ``url`` - The direct url to the uploaded file
- * ``markdown`` - Markdown for the uploaded file
- """
- if filepath is None and filedata is None:
- raise exc.GitlabUploadError("No file contents or path specified")
-
- if filedata is not None and filepath is not None:
- raise exc.GitlabUploadError("File contents and file path specified")
-
- if filepath is not None:
- with open(filepath, "rb") as f:
- filedata = f.read()
-
- url = "/projects/%(id)s/uploads" % {"id": self.id}
- file_info = {"file": (filename, filedata)}
- data = self.manager.gitlab.http_post(url, files=file_info)
-
- if TYPE_CHECKING:
- assert isinstance(data, dict)
- return {"alt": data["alt"], "url": data["url"], "markdown": data["markdown"]}
-
- @cli.register_custom_action("Project", optional=("wiki",))
- @exc.on_http_error(exc.GitlabGetError)
- def snapshot(
- self,
- wiki: bool = False,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Return a snapshot of the repository.
-
- Args:
- wiki (bool): If True return the wiki repository
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The uncompressed tar archive of the repository
- """
- path = "/projects/%s/snapshot" % self.get_id()
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("scope", "search"))
- @exc.on_http_error(exc.GitlabSearchError)
- def search(
- self, scope: str, search: str, **kwargs: Any
- ) -> Union[client.GitlabList, List[Dict[str, Any]]]:
- """Search the project resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- path = "/projects/%s/search" % self.get_id()
- return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def mirror_pull(self, **kwargs: Any) -> None:
- """Start the pull mirroring process for the project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/mirror/pull" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Project", ("to_namespace",))
- @exc.on_http_error(exc.GitlabTransferProjectError)
- def transfer_project(self, to_namespace: str, **kwargs: Any) -> None:
- """Transfer a project to the given namespace ID
-
- Args:
- to_namespace (str): ID or path of the namespace to transfer the
- project to
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTransferProjectError: If the project could not be transferred
- """
- path = "/projects/%s/transfer" % (self.id,)
- self.manager.gitlab.http_put(
- path, post_data={"namespace": to_namespace}, **kwargs
- )
-
- @cli.register_custom_action("Project", ("ref_name", "job"), ("job_token",))
- @exc.on_http_error(exc.GitlabGetError)
- def artifacts(
- self,
- ref_name: str,
- job: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Get the job artifacts archive from a specific tag or branch.
-
- Args:
- ref_name (str): Branch or tag name in repository. HEAD or SHA references
- are not supported.
- artifact_path (str): Path to a file inside the artifacts archive.
- job (str): The name of the job.
- job_token (str): Job token for multi-project pipeline triggers.
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "/projects/%s/jobs/artifacts/%s/download" % (self.get_id(), ref_name)
- result = self.manager.gitlab.http_get(
- path, job=job, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("ref_name", "artifact_path", "job"))
- @exc.on_http_error(exc.GitlabGetError)
- def artifact(
- self,
- ref_name: str,
- artifact_path: str,
- job: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Download a single artifact file from a specific tag or branch from within the job’s artifacts archive.
-
- Args:
- ref_name (str): Branch or tag name in repository. HEAD or SHA references are not supported.
- artifact_path (str): Path to a file inside the artifacts archive.
- job (str): The name of the job.
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
-
- path = "/projects/%s/jobs/artifacts/%s/raw/%s?job=%s" % (
- self.get_id(),
- ref_name,
- artifact_path,
- job,
- )
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectManager(CRUDMixin, RESTManager):
- _path = "/projects"
- _obj_cls = Project
- # Please keep these _create_attrs in same order as they are at:
- # https://docs.gitlab.com/ee/api/projects.html#create-project
- _create_attrs = RequiredOptional(
- optional=(
- "name",
- "path",
- "allow_merge_on_skipped_pipeline",
- "analytics_access_level",
- "approvals_before_merge",
- "auto_cancel_pending_pipelines",
- "auto_devops_deploy_strategy",
- "auto_devops_enabled",
- "autoclose_referenced_issues",
- "avatar",
- "build_coverage_regex",
- "build_git_strategy",
- "build_timeout",
- "builds_access_level",
- "ci_config_path",
- "container_expiration_policy_attributes",
- "container_registry_enabled",
- "default_branch",
- "description",
- "emails_disabled",
- "external_authorization_classification_label",
- "forking_access_level",
- "group_with_project_templates_id",
- "import_url",
- "initialize_with_readme",
- "issues_access_level",
- "issues_enabled",
- "jobs_enabled",
- "lfs_enabled",
- "merge_method",
- "merge_requests_access_level",
- "merge_requests_enabled",
- "mirror_trigger_builds",
- "mirror",
- "namespace_id",
- "operations_access_level",
- "only_allow_merge_if_all_discussions_are_resolved",
- "only_allow_merge_if_pipeline_succeeds",
- "packages_enabled",
- "pages_access_level",
- "requirements_access_level",
- "printing_merge_request_link_enabled",
- "public_builds",
- "remove_source_branch_after_merge",
- "repository_access_level",
- "repository_storage",
- "request_access_enabled",
- "resolve_outdated_diff_discussions",
- "shared_runners_enabled",
- "show_default_award_emojis",
- "snippets_access_level",
- "snippets_enabled",
- "tag_list",
- "template_name",
- "template_project_id",
- "use_custom_template",
- "visibility",
- "wiki_access_level",
- "wiki_enabled",
- ),
- )
- # Please keep these _update_attrs in same order as they are at:
- # https://docs.gitlab.com/ee/api/projects.html#edit-project
- _update_attrs = RequiredOptional(
- optional=(
- "allow_merge_on_skipped_pipeline",
- "analytics_access_level",
- "approvals_before_merge",
- "auto_cancel_pending_pipelines",
- "auto_devops_deploy_strategy",
- "auto_devops_enabled",
- "autoclose_referenced_issues",
- "avatar",
- "build_coverage_regex",
- "build_git_strategy",
- "build_timeout",
- "builds_access_level",
- "ci_config_path",
- "ci_default_git_depth",
- "ci_forward_deployment_enabled",
- "container_expiration_policy_attributes",
- "container_registry_enabled",
- "default_branch",
- "description",
- "emails_disabled",
- "external_authorization_classification_label",
- "forking_access_level",
- "import_url",
- "issues_access_level",
- "issues_enabled",
- "jobs_enabled",
- "lfs_enabled",
- "merge_method",
- "merge_requests_access_level",
- "merge_requests_enabled",
- "mirror_overwrites_diverged_branches",
- "mirror_trigger_builds",
- "mirror_user_id",
- "mirror",
- "name",
- "operations_access_level",
- "only_allow_merge_if_all_discussions_are_resolved",
- "only_allow_merge_if_pipeline_succeeds",
- "only_mirror_protected_branches",
- "packages_enabled",
- "pages_access_level",
- "requirements_access_level",
- "restrict_user_defined_variables",
- "path",
- "public_builds",
- "remove_source_branch_after_merge",
- "repository_access_level",
- "repository_storage",
- "request_access_enabled",
- "resolve_outdated_diff_discussions",
- "service_desk_enabled",
- "shared_runners_enabled",
- "show_default_award_emojis",
- "snippets_access_level",
- "snippets_enabled",
- "suggestion_commit_message",
- "tag_list",
- "visibility",
- "wiki_access_level",
- "wiki_enabled",
- "issues_template",
- "merge_requests_template",
- ),
- )
- _list_filters = (
- "archived",
- "id_after",
- "id_before",
- "last_activity_after",
- "last_activity_before",
- "membership",
- "min_access_level",
- "order_by",
- "owned",
- "repository_checksum_failed",
- "repository_storage",
- "search_namespaces",
- "search",
- "simple",
- "sort",
- "starred",
- "statistics",
- "topic",
- "visibility",
- "wiki_checksum_failed",
- "with_custom_attributes",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_programming_language",
- )
- _types = {"avatar": types.ImageAttribute, "topic": types.ListAttribute}
-
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Project:
- return cast(Project, super().get(id=id, lazy=lazy, **kwargs))
-
- def import_project(
- self,
- file: str,
- path: str,
- name: Optional[str] = None,
- namespace: Optional[str] = None,
- overwrite: bool = False,
- override_params: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from an archive file.
-
- Args:
- file: Data or file object containing the project
- path (str): Name and path for the new project
- namespace (str): The ID or path of the namespace that the project
- will be imported to
- overwrite (bool): If True overwrite an existing project with the
- same path
- override_params (dict): Set the specific settings for the project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
- """
- files = {"file": ("file.tar.gz", file, "application/octet-stream")}
- data = {"path": path, "overwrite": str(overwrite)}
- if override_params:
- for k, v in override_params.items():
- data["override_params[%s]" % k] = v
- if name is not None:
- data["name"] = name
- if namespace:
- data["namespace"] = namespace
- return self.gitlab.http_post(
- "/projects/import", post_data=data, files=files, **kwargs
- )
-
- def import_bitbucket_server(
- self,
- bitbucket_server_url: str,
- bitbucket_server_username: str,
- personal_access_token: str,
- bitbucket_server_project: str,
- bitbucket_server_repo: str,
- new_name: Optional[str] = None,
- target_namespace: Optional[str] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from BitBucket Server to Gitlab (schedule the import)
-
- This method will return when an import operation has been safely queued,
- or an error has occurred. After triggering an import, check the
- ``import_status`` of the newly created project to detect when the import
- operation has completed.
-
- .. note::
- This request may take longer than most other API requests.
- So this method will specify a 60 second default timeout if none is specified.
- A timeout can be specified via kwargs to override this functionality.
-
- Args:
- bitbucket_server_url (str): Bitbucket Server URL
- bitbucket_server_username (str): Bitbucket Server Username
- personal_access_token (str): Bitbucket Server personal access
- token/password
- bitbucket_server_project (str): Bitbucket Project Key
- bitbucket_server_repo (str): Bitbucket Repository Name
- new_name (str): New repository name (Optional)
- target_namespace (str): Namespace to import repository into.
- Supports subgroups like /namespace/subgroup (Optional)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
-
- Example:
-
- .. code-block:: python
-
- gl = gitlab.Gitlab_from_config()
- print("Triggering import")
- result = gl.projects.import_bitbucket_server(
- bitbucket_server_url="https://some.server.url",
- bitbucket_server_username="some_bitbucket_user",
- personal_access_token="my_password_or_access_token",
- bitbucket_server_project="my_project",
- bitbucket_server_repo="my_repo",
- new_name="gl_project_name",
- target_namespace="gl_project_path"
- )
- project = gl.projects.get(ret['id'])
- print("Waiting for import to complete")
- while project.import_status == u'started':
- time.sleep(1.0)
- project = gl.projects.get(project.id)
- print("BitBucket import complete")
-
- """
- data = {
- "bitbucket_server_url": bitbucket_server_url,
- "bitbucket_server_username": bitbucket_server_username,
- "personal_access_token": personal_access_token,
- "bitbucket_server_project": bitbucket_server_project,
- "bitbucket_server_repo": bitbucket_server_repo,
- }
- if new_name:
- data["new_name"] = new_name
- if target_namespace:
- data["target_namespace"] = target_namespace
- if (
- "timeout" not in kwargs
- or self.gitlab.timeout is None
- or self.gitlab.timeout < 60.0
- ):
- # Ensure that this HTTP request has a longer-than-usual default timeout
- # The base gitlab object tends to have a default that is <10 seconds,
- # and this is too short for this API command, typically.
- # On the order of 24 seconds has been measured on a typical gitlab instance.
- kwargs["timeout"] = 60.0
- result = self.gitlab.http_post(
- "/import/bitbucket_server", post_data=data, **kwargs
- )
- return result
-
- def import_github(
- self,
- personal_access_token: str,
- repo_id: int,
- target_namespace: str,
- new_name: Optional[str] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from Github to Gitlab (schedule the import)
-
- This method will return when an import operation has been safely queued,
- or an error has occurred. After triggering an import, check the
- ``import_status`` of the newly created project to detect when the import
- operation has completed.
-
- .. note::
- This request may take longer than most other API requests.
- So this method will specify a 60 second default timeout if none is specified.
- A timeout can be specified via kwargs to override this functionality.
-
- Args:
- personal_access_token (str): GitHub personal access token
- repo_id (int): Github repository ID
- target_namespace (str): Namespace to import repo into
- new_name (str): New repo name (Optional)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
-
- Example:
-
- .. code-block:: python
-
- gl = gitlab.Gitlab_from_config()
- print("Triggering import")
- result = gl.projects.import_github(ACCESS_TOKEN,
- 123456,
- "my-group/my-subgroup")
- project = gl.projects.get(ret['id'])
- print("Waiting for import to complete")
- while project.import_status == u'started':
- time.sleep(1.0)
- project = gl.projects.get(project.id)
- print("Github import complete")
-
- """
- data = {
- "personal_access_token": personal_access_token,
- "repo_id": repo_id,
- "target_namespace": target_namespace,
- }
- if new_name:
- data["new_name"] = new_name
- if (
- "timeout" not in kwargs
- or self.gitlab.timeout is None
- or self.gitlab.timeout < 60.0
- ):
- # Ensure that this HTTP request has a longer-than-usual default timeout
- # The base gitlab object tends to have a default that is <10 seconds,
- # and this is too short for this API command, typically.
- # On the order of 24 seconds has been measured on a typical gitlab instance.
- kwargs["timeout"] = 60.0
- result = self.gitlab.http_post("/import/github", post_data=data, **kwargs)
- return result
-
-
-class ProjectFork(RESTObject):
- pass
-
-
-class ProjectForkManager(CreateMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/forks"
- _obj_cls = ProjectFork
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "membership",
- "starred",
- "statistics",
- "with_custom_attributes",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- )
- _create_attrs = RequiredOptional(optional=("namespace",))
-
- def create(
- self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> ProjectFork:
- """Creates a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the managed object class build with
- the data sent by the server
- """
- if TYPE_CHECKING:
- assert self.path is not None
- path = self.path[:-1] # drop the 's'
- return cast(ProjectFork, CreateMixin.create(self, data, path=path, **kwargs))
-
-
-class ProjectRemoteMirror(SaveMixin, RESTObject):
- pass
-
-
-class ProjectRemoteMirrorManager(ListMixin, CreateMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/remote_mirrors"
- _obj_cls = ProjectRemoteMirror
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",), optional=("enabled", "only_protected_branches")
- )
- _update_attrs = RequiredOptional(optional=("enabled", "only_protected_branches"))
diff --git a/gitlab/v4/objects/push_rules.py b/gitlab/v4/objects/push_rules.py
deleted file mode 100644
index ee20f96..0000000
--- a/gitlab/v4/objects/push_rules.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectPushRules",
- "ProjectPushRulesManager",
-]
-
-
-class ProjectPushRules(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectPushRulesManager(
- GetWithoutIdMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/push_rule"
- _obj_cls = ProjectPushRules
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- optional=(
- "deny_delete_tag",
- "member_check",
- "prevent_secrets",
- "commit_message_regex",
- "branch_name_regex",
- "author_email_regex",
- "file_name_regex",
- "max_file_size",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "deny_delete_tag",
- "member_check",
- "prevent_secrets",
- "commit_message_regex",
- "branch_name_regex",
- "author_email_regex",
- "file_name_regex",
- "max_file_size",
- ),
- )
diff --git a/gitlab/v4/objects/releases.py b/gitlab/v4/objects/releases.py
deleted file mode 100644
index 2af3248..0000000
--- a/gitlab/v4/objects/releases.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectRelease",
- "ProjectReleaseManager",
- "ProjectReleaseLink",
- "ProjectReleaseLinkManager",
-]
-
-
-class ProjectRelease(SaveMixin, RESTObject):
- _id_attr = "tag_name"
-
- links: "ProjectReleaseLinkManager"
-
-
-class ProjectReleaseManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/releases"
- _obj_cls = ProjectRelease
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("tag_name", "description"), optional=("name", "ref", "assets")
- )
- _update_attrs = RequiredOptional(
- optional=("name", "description", "milestones", "released_at")
- )
-
-
-class ProjectReleaseLink(ObjectDeleteMixin, SaveMixin, RESTObject):
- pass
-
-
-class ProjectReleaseLinkManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/releases/%(tag_name)s/assets/links"
- _obj_cls = ProjectReleaseLink
- _from_parent_attrs = {"project_id": "project_id", "tag_name": "tag_name"}
- _create_attrs = RequiredOptional(
- required=("name", "url"), optional=("filepath", "link_type")
- )
- _update_attrs = RequiredOptional(optional=("name", "url", "filepath", "link_type"))
diff --git a/gitlab/v4/objects/repositories.py b/gitlab/v4/objects/repositories.py
deleted file mode 100644
index de5f0d2..0000000
--- a/gitlab/v4/objects/repositories.py
+++ /dev/null
@@ -1,207 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/repositories.html
-
-Currently this module only contains repository-related methods for projects.
-"""
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-
-
-class RepositoryMixin:
- @cli.register_custom_action("Project", ("submodule", "branch", "commit_sha"))
- @exc.on_http_error(exc.GitlabUpdateError)
- def update_submodule(self, submodule, branch, commit_sha, **kwargs):
- """Update a project submodule
-
- Args:
- submodule (str): Full path to the submodule
- branch (str): Name of the branch to commit into
- commit_sha (str): Full commit SHA to update the submodule to
- commit_message (str): Commit message. If no message is provided, a default one will be set (optional)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPutError: If the submodule could not be updated
- """
-
- submodule = submodule.replace("/", "%2F") # .replace('.', '%2E')
- path = "/projects/%s/repository/submodules/%s" % (self.get_id(), submodule)
- data = {"branch": branch, "commit_sha": commit_sha}
- if "commit_message" in kwargs:
- data["commit_message"] = kwargs["commit_message"]
- return self.manager.gitlab.http_put(path, post_data=data)
-
- @cli.register_custom_action("Project", tuple(), ("path", "ref", "recursive"))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_tree(self, path="", ref="", recursive=False, **kwargs):
- """Return a list of files in the repository.
-
- Args:
- path (str): Path of the top folder (/ by default)
- ref (str): Reference to a commit or branch
- recursive (bool): Whether to get the tree recursively
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The representation of the tree
- """
- gl_path = "/projects/%s/repository/tree" % self.get_id()
- query_data = {"recursive": recursive}
- if path:
- query_data["path"] = path
- if ref:
- query_data["ref"] = ref
- return self.manager.gitlab.http_list(gl_path, query_data=query_data, **kwargs)
-
- @cli.register_custom_action("Project", ("sha",))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_blob(self, sha, **kwargs):
- """Return a file by blob SHA.
-
- Args:
- sha(str): ID of the blob
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- dict: The blob content and metadata
- """
-
- path = "/projects/%s/repository/blobs/%s" % (self.get_id(), sha)
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("Project", ("sha",))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_raw_blob(
- self, sha, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return the raw file contents for a blob.
-
- Args:
- sha(str): ID of the blob
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The blob content if streamed is False, None otherwise
- """
- path = "/projects/%s/repository/blobs/%s/raw" % (self.get_id(), sha)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("from_", "to"))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_compare(self, from_, to, **kwargs):
- """Return a diff between two branches/commits.
-
- Args:
- from_(str): Source branch/SHA
- to(str): Destination branch/SHA
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The diff
- """
- path = "/projects/%s/repository/compare" % self.get_id()
- query_data = {"from": from_, "to": to}
- return self.manager.gitlab.http_get(path, query_data=query_data, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabGetError)
- def repository_contributors(self, **kwargs):
- """Return a list of contributors for the project.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The contributors
- """
- path = "/projects/%s/repository/contributors" % self.get_id()
- return self.manager.gitlab.http_list(path, **kwargs)
-
- @cli.register_custom_action("Project", tuple(), ("sha",))
- @exc.on_http_error(exc.GitlabListError)
- def repository_archive(
- self, sha=None, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return a tarball of the repository.
-
- Args:
- sha (str): ID of the commit (default branch by default)
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- bytes: The binary data of the archive
- """
- path = "/projects/%s/repository/archive" % self.get_id()
- query_data = {}
- if sha:
- query_data["sha"] = sha
- result = self.manager.gitlab.http_get(
- path, query_data=query_data, raw=True, streamed=streamed, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_merged_branches(self, **kwargs):
- """Delete merged branches.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/repository/merged_branches" % self.get_id()
- self.manager.gitlab.http_delete(path, **kwargs)
diff --git a/gitlab/v4/objects/runners.py b/gitlab/v4/objects/runners.py
deleted file mode 100644
index a32dc84..0000000
--- a/gitlab/v4/objects/runners.py
+++ /dev/null
@@ -1,140 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CRUDMixin,
- ListMixin,
- NoUpdateMixin,
- ObjectDeleteMixin,
- SaveMixin,
-)
-
-__all__ = [
- "RunnerJob",
- "RunnerJobManager",
- "Runner",
- "RunnerManager",
- "GroupRunner",
- "GroupRunnerManager",
- "ProjectRunner",
- "ProjectRunnerManager",
-]
-
-
-class RunnerJob(RESTObject):
- pass
-
-
-class RunnerJobManager(ListMixin, RESTManager):
- _path = "/runners/%(runner_id)s/jobs"
- _obj_cls = RunnerJob
- _from_parent_attrs = {"runner_id": "id"}
- _list_filters = ("status",)
-
-
-class Runner(SaveMixin, ObjectDeleteMixin, RESTObject):
- jobs: RunnerJobManager
-
-
-class RunnerManager(CRUDMixin, RESTManager):
- _path = "/runners"
- _obj_cls = Runner
- _create_attrs = RequiredOptional(
- required=("token",),
- optional=(
- "description",
- "info",
- "active",
- "locked",
- "run_untagged",
- "tag_list",
- "access_level",
- "maximum_timeout",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "description",
- "active",
- "tag_list",
- "run_untagged",
- "locked",
- "access_level",
- "maximum_timeout",
- ),
- )
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
-
- @cli.register_custom_action("RunnerManager", tuple(), ("scope",))
- @exc.on_http_error(exc.GitlabListError)
- def all(self, scope=None, **kwargs):
- """List all the runners.
-
- Args:
- scope (str): The scope of runners to show, one of: specific,
- shared, active, paused, online
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- list(Runner): a list of runners matching the scope.
- """
- path = "/runners/all"
- query_data = {}
- if scope is not None:
- query_data["scope"] = scope
- obj = self.gitlab.http_list(path, query_data, **kwargs)
- return [self._obj_cls(self, item) for item in obj]
-
- @cli.register_custom_action("RunnerManager", ("token",))
- @exc.on_http_error(exc.GitlabVerifyError)
- def verify(self, token, **kwargs):
- """Validates authentication credentials for a registered Runner.
-
- Args:
- token (str): The runner's authentication token
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabVerifyError: If the server failed to verify the token
- """
- path = "/runners/verify"
- post_data = {"token": token}
- self.gitlab.http_post(path, post_data=post_data, **kwargs)
-
-
-class GroupRunner(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupRunnerManager(NoUpdateMixin, RESTManager):
- _path = "/groups/%(group_id)s/runners"
- _obj_cls = GroupRunner
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("runner_id",))
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
-
-
-class ProjectRunner(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectRunnerManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/runners"
- _obj_cls = ProjectRunner
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("runner_id",))
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
diff --git a/gitlab/v4/objects/services.py b/gitlab/v4/objects/services.py
deleted file mode 100644
index 6aedc39..0000000
--- a/gitlab/v4/objects/services.py
+++ /dev/null
@@ -1,303 +0,0 @@
-from gitlab import cli
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import (
- DeleteMixin,
- GetMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectService",
- "ProjectServiceManager",
-]
-
-
-class ProjectService(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/services"
- _from_parent_attrs = {"project_id": "id"}
- _obj_cls = ProjectService
-
- _service_attrs = {
- "asana": (("api_key",), ("restrict_to_branch", "push_events")),
- "assembla": (("token",), ("subdomain", "push_events")),
- "bamboo": (
- ("bamboo_url", "build_key", "username", "password"),
- ("push_events",),
- ),
- "bugzilla": (
- ("new_issue_url", "issues_url", "project_url"),
- ("description", "title", "push_events"),
- ),
- "buildkite": (
- ("token", "project_url"),
- ("enable_ssl_verification", "push_events"),
- ),
- "campfire": (("token",), ("subdomain", "room", "push_events")),
- "circuit": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "custom-issue-tracker": (
- ("new_issue_url", "issues_url", "project_url"),
- ("description", "title", "push_events"),
- ),
- "drone-ci": (
- ("token", "drone_url"),
- (
- "enable_ssl_verification",
- "push_events",
- "merge_requests_events",
- "tag_push_events",
- ),
- ),
- "emails-on-push": (
- ("recipients",),
- (
- "disable_diffs",
- "send_from_committer_email",
- "push_events",
- "tag_push_events",
- "branches_to_be_notified",
- ),
- ),
- "pipelines-email": (
- ("recipients",),
- (
- "add_pusher",
- "notify_only_broken_builds",
- "branches_to_be_notified",
- "notify_only_default_branch",
- "pipeline_events",
- ),
- ),
- "external-wiki": (("external_wiki_url",), tuple()),
- "flowdock": (("token",), ("push_events",)),
- "github": (("token", "repository_url"), ("static_context",)),
- "hangouts-chat": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "hipchat": (
- ("token",),
- (
- "color",
- "notify",
- "room",
- "api_version",
- "server",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- ),
- ),
- "irker": (
- ("recipients",),
- (
- "default_irc_uri",
- "server_port",
- "server_host",
- "colorize_messages",
- "push_events",
- ),
- ),
- "jira": (
- (
- "url",
- "username",
- "password",
- ),
- (
- "api_url",
- "active",
- "jira_issue_transition_id",
- "commit_events",
- "merge_requests_events",
- "comment_on_event_enabled",
- ),
- ),
- "slack-slash-commands": (("token",), tuple()),
- "mattermost-slash-commands": (("token",), ("username",)),
- "packagist": (
- ("username", "token"),
- ("server", "push_events", "merge_requests_events", "tag_push_events"),
- ),
- "mattermost": (
- ("webhook",),
- (
- "username",
- "channel",
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- "push_channel",
- "issue_channel",
- "confidential_issue_channel" "merge_request_channel",
- "note_channel",
- "confidential_note_channel",
- "tag_push_channel",
- "pipeline_channel",
- "wiki_page_channel",
- ),
- ),
- "pivotaltracker": (("token",), ("restrict_to_branch", "push_events")),
- "prometheus": (("api_url",), tuple()),
- "pushover": (
- ("api_key", "user_key", "priority"),
- ("device", "sound", "push_events"),
- ),
- "redmine": (
- ("new_issue_url", "project_url", "issues_url"),
- ("description", "push_events"),
- ),
- "slack": (
- ("webhook",),
- (
- "username",
- "channel",
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "commit_events",
- "confidential_issue_channel",
- "confidential_issues_events",
- "confidential_note_channel",
- "confidential_note_events",
- "deployment_channel",
- "deployment_events",
- "issue_channel",
- "issues_events",
- "job_events",
- "merge_request_channel",
- "merge_requests_events",
- "note_channel",
- "note_events",
- "pipeline_channel",
- "pipeline_events",
- "push_channel",
- "push_events",
- "tag_push_channel",
- "tag_push_events",
- "wiki_page_channel",
- "wiki_page_events",
- ),
- ),
- "microsoft-teams": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "teamcity": (
- ("teamcity_url", "build_type", "username", "password"),
- ("push_events",),
- ),
- "jenkins": (("jenkins_url", "project_name"), ("username", "password")),
- "mock-ci": (("mock_service_url",), tuple()),
- "youtrack": (("issues_url", "project_url"), ("description", "push_events")),
- }
-
- def get(self, id, **kwargs):
- """Retrieve a single object.
-
- Args:
- id (int or str): ID of the object to retrieve
- lazy (bool): If True, don't request the server, but create a
- shallow object giving access to the managers. This is
- useful if you want to avoid useless calls to the API.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject.
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- obj = super(ProjectServiceManager, self).get(id, **kwargs)
- obj.id = id
- return obj
-
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- super(ProjectServiceManager, self).update(id, new_data, **kwargs)
- self.id = id
-
- @cli.register_custom_action("ProjectServiceManager")
- def available(self, **kwargs):
- """List the services known by python-gitlab.
-
- Returns:
- list (str): The list of service code names.
- """
- return list(self._service_attrs.keys())
diff --git a/gitlab/v4/objects/settings.py b/gitlab/v4/objects/settings.py
deleted file mode 100644
index 1c8be25..0000000
--- a/gitlab/v4/objects/settings.py
+++ /dev/null
@@ -1,109 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "ApplicationSettings",
- "ApplicationSettingsManager",
-]
-
-
-class ApplicationSettings(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ApplicationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/application/settings"
- _obj_cls = ApplicationSettings
- _update_attrs = RequiredOptional(
- optional=(
- "id",
- "default_projects_limit",
- "signup_enabled",
- "password_authentication_enabled_for_web",
- "gravatar_enabled",
- "sign_in_text",
- "created_at",
- "updated_at",
- "home_page_url",
- "default_branch_protection",
- "restricted_visibility_levels",
- "max_attachment_size",
- "session_expire_delay",
- "default_project_visibility",
- "default_snippet_visibility",
- "default_group_visibility",
- "outbound_local_requests_whitelist",
- "disabled_oauth_sign_in_sources",
- "domain_whitelist",
- "domain_blacklist_enabled",
- "domain_blacklist",
- "domain_allowlist",
- "domain_denylist_enabled",
- "domain_denylist",
- "external_authorization_service_enabled",
- "external_authorization_service_url",
- "external_authorization_service_default_label",
- "external_authorization_service_timeout",
- "import_sources",
- "user_oauth_applications",
- "after_sign_out_path",
- "container_registry_token_expire_delay",
- "repository_storages",
- "plantuml_enabled",
- "plantuml_url",
- "terminal_max_session_time",
- "polling_interval_multiplier",
- "rsa_key_restriction",
- "dsa_key_restriction",
- "ecdsa_key_restriction",
- "ed25519_key_restriction",
- "first_day_of_week",
- "enforce_terms",
- "terms",
- "performance_bar_allowed_group_id",
- "instance_statistics_visibility_private",
- "user_show_add_ssh_key_message",
- "file_template_project_id",
- "local_markdown_version",
- "asset_proxy_enabled",
- "asset_proxy_url",
- "asset_proxy_whitelist",
- "asset_proxy_allowlist",
- "geo_node_allowed_ips",
- "allow_local_requests_from_hooks_and_services",
- "allow_local_requests_from_web_hooks_and_services",
- "allow_local_requests_from_system_hooks",
- ),
- )
- _types = {
- "asset_proxy_allowlist": types.ListAttribute,
- "disabled_oauth_sign_in_sources": types.ListAttribute,
- "domain_allowlist": types.ListAttribute,
- "domain_denylist": types.ListAttribute,
- "import_sources": types.ListAttribute,
- "restricted_visibility_levels": types.ListAttribute,
- }
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- if "domain_whitelist" in data and data["domain_whitelist"] is None:
- data.pop("domain_whitelist")
- super(ApplicationSettingsManager, self).update(id, data, **kwargs)
diff --git a/gitlab/v4/objects/sidekiq.py b/gitlab/v4/objects/sidekiq.py
deleted file mode 100644
index dc1094a..0000000
--- a/gitlab/v4/objects/sidekiq.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager
-
-__all__ = [
- "SidekiqManager",
-]
-
-
-class SidekiqManager(RESTManager):
- """Manager for the Sidekiq methods.
-
- This manager doesn't actually manage objects but provides helper function
- for the sidekiq metrics API.
- """
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def queue_metrics(self, **kwargs):
- """Return the registered queues information.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Information about the Sidekiq queues
- """
- return self.gitlab.http_get("/sidekiq/queue_metrics", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def process_metrics(self, **kwargs):
- """Return the registered sidekiq workers.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Information about the register Sidekiq worker
- """
- return self.gitlab.http_get("/sidekiq/process_metrics", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def job_stats(self, **kwargs):
- """Return statistics about the jobs performed.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Statistics about the Sidekiq jobs performed
- """
- return self.gitlab.http_get("/sidekiq/job_stats", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def compound_metrics(self, **kwargs):
- """Return all available metrics and statistics.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: All available Sidekiq metrics and statistics
- """
- return self.gitlab.http_get("/sidekiq/compound_metrics", **kwargs)
diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py
deleted file mode 100644
index 164b30c..0000000
--- a/gitlab/v4/objects/snippets.py
+++ /dev/null
@@ -1,123 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin, UserAgentDetailMixin
-
-from .award_emojis import ProjectSnippetAwardEmojiManager # noqa: F401
-from .discussions import ProjectSnippetDiscussionManager # noqa: F401
-from .notes import ProjectSnippetNoteManager # noqa: F401
-
-__all__ = [
- "Snippet",
- "SnippetManager",
- "ProjectSnippet",
- "ProjectSnippetManager",
-]
-
-
-class Snippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("Snippet")
- @exc.on_http_error(exc.GitlabGetError)
- def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The snippet content
- """
- path = "/snippets/%s/raw" % self.get_id()
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class SnippetManager(CRUDMixin, RESTManager):
- _path = "/snippets"
- _obj_cls = Snippet
- _create_attrs = RequiredOptional(
- required=("title", "file_name", "content"), optional=("lifetime", "visibility")
- )
- _update_attrs = RequiredOptional(
- optional=("title", "file_name", "content", "visibility")
- )
-
- @cli.register_custom_action("SnippetManager")
- def public(self, **kwargs):
- """List all the public snippets.
-
- Args:
- all (bool): If True the returned object will be a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: A generator for the snippets list
- """
- return self.list(path="/snippets/public", **kwargs)
-
-
-class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _url = "/projects/%(project_id)s/snippets"
- _short_print_attr = "title"
-
- awardemojis: ProjectSnippetAwardEmojiManager
- discussions: ProjectSnippetDiscussionManager
- notes: ProjectSnippetNoteManager
-
- @cli.register_custom_action("ProjectSnippet")
- @exc.on_http_error(exc.GitlabGetError)
- def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The snippet content
- """
- path = "%s/%s/raw" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectSnippetManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets"
- _obj_cls = ProjectSnippet
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "file_name", "content", "visibility"),
- optional=("description",),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "file_name", "content", "visibility", "description"),
- )
diff --git a/gitlab/v4/objects/statistics.py b/gitlab/v4/objects/statistics.py
deleted file mode 100644
index 5d7c19e..0000000
--- a/gitlab/v4/objects/statistics.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, RefreshMixin
-
-__all__ = [
- "GroupIssuesStatistics",
- "GroupIssuesStatisticsManager",
- "ProjectAdditionalStatistics",
- "ProjectAdditionalStatisticsManager",
- "IssuesStatistics",
- "IssuesStatisticsManager",
- "ProjectIssuesStatistics",
- "ProjectIssuesStatisticsManager",
-]
-
-
-class ProjectAdditionalStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectAdditionalStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/statistics"
- _obj_cls = ProjectAdditionalStatistics
- _from_parent_attrs = {"project_id": "id"}
-
-
-class IssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class IssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/issues_statistics"
- _obj_cls = IssuesStatistics
-
-
-class GroupIssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class GroupIssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/groups/%(group_id)s/issues_statistics"
- _obj_cls = GroupIssuesStatistics
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectIssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectIssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues_statistics"
- _obj_cls = ProjectIssuesStatistics
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/tags.py b/gitlab/v4/objects/tags.py
deleted file mode 100644
index 44fc23c..0000000
--- a/gitlab/v4/objects/tags.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectTag",
- "ProjectTagManager",
- "ProjectProtectedTag",
- "ProjectProtectedTagManager",
-]
-
-
-class ProjectTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
- _short_print_attr = "name"
-
-
-class ProjectTagManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/tags"
- _obj_cls = ProjectTag
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("tag_name", "ref"), optional=("message",)
- )
-
-
-class ProjectProtectedTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
- _short_print_attr = "name"
-
-
-class ProjectProtectedTagManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/protected_tags"
- _obj_cls = ProjectProtectedTag
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",), optional=("create_access_level",)
- )
diff --git a/gitlab/v4/objects/templates.py b/gitlab/v4/objects/templates.py
deleted file mode 100644
index 04de463..0000000
--- a/gitlab/v4/objects/templates.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "Dockerfile",
- "DockerfileManager",
- "Gitignore",
- "GitignoreManager",
- "Gitlabciyml",
- "GitlabciymlManager",
- "License",
- "LicenseManager",
-]
-
-
-class Dockerfile(RESTObject):
- _id_attr = "name"
-
-
-class DockerfileManager(RetrieveMixin, RESTManager):
- _path = "/templates/dockerfiles"
- _obj_cls = Dockerfile
-
-
-class Gitignore(RESTObject):
- _id_attr = "name"
-
-
-class GitignoreManager(RetrieveMixin, RESTManager):
- _path = "/templates/gitignores"
- _obj_cls = Gitignore
-
-
-class Gitlabciyml(RESTObject):
- _id_attr = "name"
-
-
-class GitlabciymlManager(RetrieveMixin, RESTManager):
- _path = "/templates/gitlab_ci_ymls"
- _obj_cls = Gitlabciyml
-
-
-class License(RESTObject):
- _id_attr = "key"
-
-
-class LicenseManager(RetrieveMixin, RESTManager):
- _path = "/templates/licenses"
- _obj_cls = License
- _list_filters = ("popular",)
- _optional_get_attrs = ("project", "fullname")
diff --git a/gitlab/v4/objects/todos.py b/gitlab/v4/objects/todos.py
deleted file mode 100644
index de82437..0000000
--- a/gitlab/v4/objects/todos.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Todo",
- "TodoManager",
-]
-
-
-class Todo(ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("Todo")
- @exc.on_http_error(exc.GitlabTodoError)
- def mark_as_done(self, **kwargs):
- """Mark the todo as done.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the server failed to perform the request
- """
- path = "%s/%s/mark_as_done" % (self.manager.path, self.id)
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
-
-class TodoManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/todos"
- _obj_cls = Todo
- _list_filters = ("action", "author_id", "project_id", "state", "type")
-
- @cli.register_custom_action("TodoManager")
- @exc.on_http_error(exc.GitlabTodoError)
- def mark_all_as_done(self, **kwargs):
- """Mark all the todos as done.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the server failed to perform the request
-
- Returns:
- int: The number of todos marked done
- """
- self.gitlab.http_post("/todos/mark_as_done", **kwargs)
diff --git a/gitlab/v4/objects/triggers.py b/gitlab/v4/objects/triggers.py
deleted file mode 100644
index f203d93..0000000
--- a/gitlab/v4/objects/triggers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectTrigger",
- "ProjectTriggerManager",
-]
-
-
-class ProjectTrigger(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectTriggerManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/triggers"
- _obj_cls = ProjectTrigger
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("description",))
- _update_attrs = RequiredOptional(required=("description",))
diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py
deleted file mode 100644
index e4bbcf1..0000000
--- a/gitlab/v4/objects/users.py
+++ /dev/null
@@ -1,514 +0,0 @@
-from typing import Any, cast, Dict, List, Union
-
-import requests
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- NoUpdateMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .custom_attributes import UserCustomAttributeManager # noqa: F401
-from .events import UserEventManager # noqa: F401
-from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401
-
-__all__ = [
- "CurrentUserEmail",
- "CurrentUserEmailManager",
- "CurrentUserGPGKey",
- "CurrentUserGPGKeyManager",
- "CurrentUserKey",
- "CurrentUserKeyManager",
- "CurrentUserStatus",
- "CurrentUserStatusManager",
- "CurrentUser",
- "CurrentUserManager",
- "User",
- "UserManager",
- "ProjectUser",
- "ProjectUserManager",
- "UserEmail",
- "UserEmailManager",
- "UserActivities",
- "UserStatus",
- "UserStatusManager",
- "UserActivitiesManager",
- "UserGPGKey",
- "UserGPGKeyManager",
- "UserKey",
- "UserKeyManager",
- "UserIdentityProviderManager",
- "UserImpersonationToken",
- "UserImpersonationTokenManager",
- "UserMembership",
- "UserMembershipManager",
- "UserProject",
- "UserProjectManager",
-]
-
-
-class CurrentUserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "email"
-
-
-class CurrentUserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/emails"
- _obj_cls = CurrentUserEmail
- _create_attrs = RequiredOptional(required=("email",))
-
-
-class CurrentUserGPGKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class CurrentUserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/gpg_keys"
- _obj_cls = CurrentUserGPGKey
- _create_attrs = RequiredOptional(required=("key",))
-
-
-class CurrentUserKey(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
-
-class CurrentUserKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/keys"
- _obj_cls = CurrentUserKey
- _create_attrs = RequiredOptional(required=("title", "key"))
-
-
-class CurrentUserStatus(SaveMixin, RESTObject):
- _id_attr = None
- _short_print_attr = "message"
-
-
-class CurrentUserStatusManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/user/status"
- _obj_cls = CurrentUserStatus
- _update_attrs = RequiredOptional(optional=("emoji", "message"))
-
-
-class CurrentUser(RESTObject):
- _id_attr = None
- _short_print_attr = "username"
-
- emails: CurrentUserEmailManager
- gpgkeys: CurrentUserGPGKeyManager
- keys: CurrentUserKeyManager
- status: CurrentUserStatusManager
-
-
-class CurrentUserManager(GetWithoutIdMixin, RESTManager):
- _path = "/user"
- _obj_cls = CurrentUser
-
-
-class User(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
- customattributes: UserCustomAttributeManager
- emails: "UserEmailManager"
- events: UserEventManager
- followers_users: "UserFollowersManager"
- following_users: "UserFollowingManager"
- gpgkeys: "UserGPGKeyManager"
- identityproviders: "UserIdentityProviderManager"
- impersonationtokens: "UserImpersonationTokenManager"
- keys: "UserKeyManager"
- memberships: "UserMembershipManager"
- personal_access_tokens: UserPersonalAccessTokenManager
- projects: "UserProjectManager"
- status: "UserStatusManager"
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabBlockError)
- def block(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Block the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabBlockError: If the user could not be blocked
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/block" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data is True:
- self._attrs["state"] = "blocked"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabFollowError)
- def follow(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Follow the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabFollowError: If the user could not be followed
-
- Returns:
- dict: The new object data (*not* a RESTObject)
- """
- path = "/users/%s/follow" % self.id
- return self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabUnfollowError)
- def unfollow(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Unfollow the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnfollowError: If the user could not be followed
-
- Returns:
- dict: The new object data (*not* a RESTObject)
- """
- path = "/users/%s/unfollow" % self.id
- return self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabUnblockError)
- def unblock(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Unblock the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnblockError: If the user could not be unblocked
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/unblock" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data is True:
- self._attrs["state"] = "active"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabDeactivateError)
- def deactivate(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Deactivate the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeactivateError: If the user could not be deactivated
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/deactivate" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data:
- self._attrs["state"] = "deactivated"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabActivateError)
- def activate(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Activate the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabActivateError: If the user could not be activated
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/activate" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data:
- self._attrs["state"] = "active"
- return server_data
-
-
-class UserManager(CRUDMixin, RESTManager):
- _path = "/users"
- _obj_cls = User
-
- _list_filters = (
- "active",
- "blocked",
- "username",
- "extern_uid",
- "provider",
- "external",
- "search",
- "custom_attributes",
- "status",
- "two_factor",
- )
- _create_attrs = RequiredOptional(
- optional=(
- "email",
- "username",
- "name",
- "password",
- "reset_password",
- "skype",
- "linkedin",
- "twitter",
- "projects_limit",
- "extern_uid",
- "provider",
- "bio",
- "admin",
- "can_create_group",
- "website_url",
- "skip_confirmation",
- "external",
- "organization",
- "location",
- "avatar",
- "public_email",
- "private_profile",
- "color_scheme_id",
- "theme_id",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("email", "username", "name"),
- optional=(
- "password",
- "skype",
- "linkedin",
- "twitter",
- "projects_limit",
- "extern_uid",
- "provider",
- "bio",
- "admin",
- "can_create_group",
- "website_url",
- "skip_reconfirmation",
- "external",
- "organization",
- "location",
- "avatar",
- "public_email",
- "private_profile",
- "color_scheme_id",
- "theme_id",
- ),
- )
- _types = {"confirm": types.LowercaseStringAttribute, "avatar": types.ImageAttribute}
-
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> User:
- return cast(User, super().get(id=id, lazy=lazy, **kwargs))
-
-
-class ProjectUser(RESTObject):
- pass
-
-
-class ProjectUserManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/users"
- _obj_cls = ProjectUser
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = ("search", "skip_users")
- _types = {"skip_users": types.ListAttribute}
-
-
-class UserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "email"
-
-
-class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/emails"
- _obj_cls = UserEmail
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("email",))
-
-
-class UserActivities(RESTObject):
- _id_attr = "username"
-
-
-class UserStatus(RESTObject):
- _id_attr = None
- _short_print_attr = "message"
-
-
-class UserStatusManager(GetWithoutIdMixin, RESTManager):
- _path = "/users/%(user_id)s/status"
- _obj_cls = UserStatus
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserActivitiesManager(ListMixin, RESTManager):
- _path = "/user/activities"
- _obj_cls = UserActivities
-
-
-class UserGPGKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/gpg_keys"
- _obj_cls = UserGPGKey
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("key",))
-
-
-class UserKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserKeyManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/keys"
- _obj_cls = UserKey
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("title", "key"))
-
-
-class UserIdentityProviderManager(DeleteMixin, RESTManager):
- """Manager for user identities.
-
- This manager does not actually manage objects but enables
- functionality for deletion of user identities by provider.
- """
-
- _path = "/users/%(user_id)s/identities"
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserImpersonationToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserImpersonationTokenManager(NoUpdateMixin, RESTManager):
- _path = "/users/%(user_id)s/impersonation_tokens"
- _obj_cls = UserImpersonationToken
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "scopes"), optional=("expires_at",)
- )
- _list_filters = ("state",)
-
-
-class UserMembership(RESTObject):
- _id_attr = "source_id"
-
-
-class UserMembershipManager(RetrieveMixin, RESTManager):
- _path = "/users/%(user_id)s/memberships"
- _obj_cls = UserMembership
- _from_parent_attrs = {"user_id": "id"}
- _list_filters = ("type",)
-
-
-# Having this outside projects avoids circular imports due to ProjectUser
-class UserProject(RESTObject):
- pass
-
-
-class UserProjectManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/user/%(user_id)s"
- _obj_cls = UserProject
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",),
- optional=(
- "default_branch",
- "issues_enabled",
- "wall_enabled",
- "merge_requests_enabled",
- "wiki_enabled",
- "snippets_enabled",
- "public",
- "visibility",
- "description",
- "builds_enabled",
- "public_builds",
- "import_url",
- "only_allow_merge_if_build_succeeds",
- ),
- )
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "membership",
- "starred",
- "statistics",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_custom_attributes",
- "with_programming_language",
- "wiki_checksum_failed",
- "repository_checksum_failed",
- "min_access_level",
- "id_after",
- "id_before",
- )
-
- def list(self, **kwargs: Any) -> Union[RESTObjectList, List[RESTObject]]:
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
- if self._parent:
- path = "/users/%s/projects" % self._parent.id
- else:
- path = "/users/%s/projects" % kwargs["user_id"]
- return ListMixin.list(self, path=path, **kwargs)
-
-
-class UserFollowersManager(ListMixin, RESTManager):
- _path = "/users/%(user_id)s/followers"
- _obj_cls = User
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserFollowingManager(ListMixin, RESTManager):
- _path = "/users/%(user_id)s/following"
- _obj_cls = User
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/variables.py b/gitlab/v4/objects/variables.py
deleted file mode 100644
index 2e5e483..0000000
--- a/gitlab/v4/objects/variables.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/instance_level_ci_variables.html
-https://docs.gitlab.com/ee/api/project_level_variables.html
-https://docs.gitlab.com/ee/api/group_level_variables.html
-"""
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "Variable",
- "VariableManager",
- "GroupVariable",
- "GroupVariableManager",
- "ProjectVariable",
- "ProjectVariableManager",
-]
-
-
-class Variable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class VariableManager(CRUDMixin, RESTManager):
- _path = "/admin/ci/variables"
- _obj_cls = Variable
- _create_attrs = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
-
-
-class GroupVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class GroupVariableManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/variables"
- _obj_cls = GroupVariable
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
-
-
-class ProjectVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class ProjectVariableManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/variables"
- _obj_cls = ProjectVariable
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("key", "value"),
- optional=("protected", "variable_type", "masked", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"),
- optional=("protected", "variable_type", "masked", "environment_scope"),
- )
diff --git a/gitlab/v4/objects/wikis.py b/gitlab/v4/objects/wikis.py
deleted file mode 100644
index a86b442..0000000
--- a/gitlab/v4/objects/wikis.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectWiki",
- "ProjectWikiManager",
- "GroupWiki",
- "GroupWikiManager",
-]
-
-
-class ProjectWiki(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "slug"
- _short_print_attr = "slug"
-
-
-class ProjectWikiManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/wikis"
- _obj_cls = ProjectWiki
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "content"), optional=("format",)
- )
- _update_attrs = RequiredOptional(optional=("title", "content", "format"))
- _list_filters = ("with_content",)
-
-
-class GroupWiki(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "slug"
- _short_print_attr = "slug"
-
-
-class GroupWikiManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/wikis"
- _obj_cls = GroupWiki
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "content"), optional=("format",)
- )
- _update_attrs = RequiredOptional(optional=("title", "content", "format"))
- _list_filters = ("with_content",)