# The Okta software accompanied by this notice is provided pursuant to the following terms:
# Copyright © 2025-Present, Okta, Inc.
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
# License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
# coding: utf-8

"""
Okta Admin Management

Allows customers to easily access the Okta Management APIs

The version of the OpenAPI document: 5.1.0
Contact: devex-public@okta.com
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""  # noqa: E501

from typing import Any, Dict, Tuple, Union
from typing import List, Optional

from pydantic import Field, StrictInt, StrictStr
from pydantic import validate_call, StrictFloat
from typing_extensions import Annotated

from okta.api_client import ApiClient, RequestSerialized
from okta.api_response import ApiResponse
from okta.models.add_group_request import AddGroupRequest
from okta.models.application import Application
from okta.models.group import Group
from okta.models.success import Success
from okta.models.user import User
from okta.rest import RESTResponse


class GroupApi(ApiClient):
    """NOTE: This class is auto generated by OpenAPI Generator
    Ref: https://openapi-generator.tech

    Do not edit the class manually.
    """

    def __init__(self, configuration) -> None:
        super().__init__(configuration=configuration)

    @validate_call
    async def add_group(
        self,
        group: AddGroupRequest,
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> Group:
        """Add a group

        Adds a new group with the `OKTA_GROUP` type to your org. > **Note:** App import operations are responsible for
        syncing groups with `APP_GROUP` type such as Active Directory groups. See [About groups](
        https://help.okta.com/okta_help.htm?id=Directory_Groups) in the help documentation.

        :param group: (required)
        :type group: AddGroupRequest
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "Group",
            "400": "Error",
            "403": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = self._add_group_serialize(
            group=group,
            _request_auth=_request_auth,
            _content_type=_content_type,
            _headers=_headers,
            _host_index=_host_index,
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if Group is Success:
                return (None, error)
            else:
                return (None, None, error)

        if Group is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, Group
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if Group is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _add_group_serialize(
        self,
        group,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter
        if group is not None:
            _body_params = group

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # set the HTTP header `Content-Type`
        if _content_type:
            _header_params["Content-Type"] = _content_type
        else:
            _default_content_type = self.select_header_content_type(
                ["application/json"]
            )
            if _default_content_type is not None:
                _header_params["Content-Type"] = _default_content_type

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="POST",
            resource_path="/api/v1/groups",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def assign_user_to_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        user_id: Annotated[StrictStr, Field(description="ID of an existing Okta user")],
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> None:
        """Assign a user to a group

        Assigns a user to a group with the `OKTA_GROUP` type. > **Note:** You only can modify memberships for groups of the
        `OKTA_GROUP` type. App imports are responsible for managing group memberships for groups of the `APP_GROUP` type,
        such as Active Directory groups.

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param user_id: ID of an existing Okta user (required)
        :type user_id: str
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "204": None,
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = (
            self._assign_user_to_group_serialize(
                group_id=group_id,
                user_id=user_id,
                _request_auth=_request_auth,
                _content_type=_content_type,
                _headers=_headers,
                _host_index=_host_index,
            )
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            return (None, error)

        response, response_body, error = await self._request_executor.execute(request)

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                return (response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _assign_user_to_group_serialize(
        self,
        group_id,
        user_id,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        if user_id is not None:
            _path_params["userId"] = user_id
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="PUT",
            resource_path="/api/v1/groups/{groupId}/users/{userId}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def delete_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> None:
        """Delete a group

        Deletes a group of the `OKTA_GROUP` or `APP_GROUP` type from your org. > **Note:** You can't remove groups of type
        `APP_GROUP` if they are used in a group push mapping.

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "204": None,
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = self._delete_group_serialize(
            group_id=group_id,
            _request_auth=_request_auth,
            _content_type=_content_type,
            _headers=_headers,
            _host_index=_host_index,
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            return (None, error)

        response, response_body, error = await self._request_executor.execute(request)

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                return (response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _delete_group_serialize(
        self,
        group_id,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="DELETE",
            resource_path="/api/v1/groups/{groupId}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def get_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> Group:
        """Retrieve a group

        Retrieves a specific group by `id` from your org

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "Group",
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = self._get_group_serialize(
            group_id=group_id,
            _request_auth=_request_auth,
            _content_type=_content_type,
            _headers=_headers,
            _host_index=_host_index,
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if Group is Success:
                return (None, error)
            else:
                return (None, None, error)

        if Group is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, Group
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if Group is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _get_group_serialize(
        self,
        group_id,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="GET",
            resource_path="/api/v1/groups/{groupId}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def list_assigned_applications_for_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        after: Annotated[
            Optional[StrictStr],
            Field(
                description="Specifies the pagination cursor for the next page of apps"
            ),
        ] = None,
        limit: Annotated[
            Optional[StrictInt],
            Field(description="Specifies the number of app results for a page"),
        ] = None,
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> List[Application]:
        """List all assigned apps

        Lists all apps that are assigned to a group. See [Application Groups API](
        /openapi/okta-management/management/tag/ApplicationGroups/).

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param after: Specifies the pagination cursor for the next page of apps
        :type after: str
        :param limit: Specifies the number of app results for a page
        :type limit: int
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "List[Application]",
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = (
            self._list_assigned_applications_for_group_serialize(
                group_id=group_id,
                after=after,
                limit=limit,
                _request_auth=_request_auth,
                _content_type=_content_type,
                _headers=_headers,
                _host_index=_host_index,
            )
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if List[Application] is Success:
                return (None, error)
            else:
                return (None, None, error)

        if List[Application] is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, Application
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if List[Application] is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _list_assigned_applications_for_group_serialize(
        self,
        group_id,
        after,
        limit,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        # process the query parameters
        if after is not None:
            _query_params.append(("after", after))

        if limit is not None:
            _query_params.append(("limit", limit))

        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="GET",
            resource_path="/api/v1/groups/{groupId}/apps",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def list_group_users(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        after: Annotated[
            Optional[StrictStr],
            Field(
                description="The cursor to use for pagination. It is an opaque string that specifies your current location in "
                            "the list and is obtained from the `Link` response header. See [Pagination]("
                            "https://developer.okta.com/docs/api/#pagination) and [Link header]("
                            "https://developer.okta.com/docs/api/#link-header)."
            ),
        ] = None,
        limit: Annotated[
            Optional[StrictInt],
            Field(description="Specifies the number of user results in a page"),
        ] = None,
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> List[User]:
        """List all member users

        Lists all users that are a member of a group. The default user limit is set to a very high number due to historical
        reasons that are no longer valid for most orgs. This will change in a future version of this API. The recommended
        page limit is now `limit=200`.

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param after: The cursor to use for pagination. It is an opaque string that specifies your current location in the
        list and is obtained from the `Link` response header. See [Pagination](
        https://developer.okta.com/docs/api/#pagination) and [Link header](https://developer.okta.com/docs/api/#link-header).
        :type after: str
        :param limit: Specifies the number of user results in a page
        :type limit: int
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "List[User]",
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = (
            self._list_group_users_serialize(
                group_id=group_id,
                after=after,
                limit=limit,
                _request_auth=_request_auth,
                _content_type=_content_type,
                _headers=_headers,
                _host_index=_host_index,
            )
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if List[User] is Success:
                return (None, error)
            else:
                return (None, None, error)

        if List[User] is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, User
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if List[User] is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _list_group_users_serialize(
        self,
        group_id,
        after,
        limit,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        # process the query parameters
        if after is not None:
            _query_params.append(("after", after))

        if limit is not None:
            _query_params.append(("limit", limit))

        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="GET",
            resource_path="/api/v1/groups/{groupId}/users",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def list_groups(
        self,
        search: Annotated[
            Optional[StrictStr],
            Field(
                description="Searches for groups with a supported [filtering](https://developer.okta.com/docs/api/#filter) "
                            "expression for all properties except for `_embedded`, `_links`, and `objectClass`. Okta "
                            "recommends "
                            "this query parameter because it provides the largest range of search options and optimal "
                            "performance.  This operation supports [pagination]("
                            "https://developer.okta.com/docs/api/#pagination).  Using search requires [URL encoding]("
                            "https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding), for example, `search=type "
                            "eq "
                            '"OKTA_GROUP"` is encoded as `search=type+eq+%22OKTA_GROUP%22`.  This operation searches many '
                            "properties:  * Any group profile attribute, including imported app group profile attributes. * "
                            "The "
                            "top-level properties: `id`, `created`, `lastMembershipUpdated`, `lastUpdated`, and `type`. * "
                            "The ["
                            "source](/openapi/okta-management/management/tag/Group/#tag/Group/operation/listGroups!c=200&path"
                            "=_links/source&t=response) of groups with type of `APP_GROUP`, accessed as `source.id`.  You can "
                            "also use the `sortBy` and `sortOrder` parameters.  Searches for groups can be filtered by the "
                            "following operators: `sw`, `eq`, and `co`. You can only use `co` with these select profile "
                            "attributes: `profile.name` and `profile.description`. See [Operators]("
                            "https://developer.okta.com/docs/api/#operators)."
            ),
        ] = None,
        filter: Annotated[
            Optional[StrictStr],
            Field(
                description="Filter expression for groups. See [Filter](https://developer.okta.com/docs/api/#filter).  "
                            "Filtering "
                            "supports the following limited number of properties: `id`, `type`, `lastUpdated`, "
                            "and `lastMembershipUpdated`.  > **Note:** All filters must be [URL encoded]("
                            "https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding). For example, "
                            '`filter=lastUpdated gt "2013-06-01T00:00:00.000Z"` is encoded as '
                            "`filter=lastUpdated%20gt%20%222013-06-01T00:00:00.000Z%22`.  See [Special characters]("
                            "https://developer.okta.com/docs/api/#special-characters)."
            ),
        ] = None,
        q: Annotated[
            Optional[StrictStr],
            Field(
                description="Finds a group that matches the `name` property. > **Note:** Paging and searching are currently "
                            "mutually exclusive. You can't page a query. The default limit for a query is 300 results. Query "
                            "is "
                            "intended for an auto-complete picker use case where users refine their search string to "
                            "constrain "
                            "the results."
            ),
        ] = None,
        after: Annotated[
            Optional[StrictStr],
            Field(
                description="Specifies the pagination cursor for the next page of groups. The `after` cursor should be "
                            "treated "
                            "as an opaque value and obtained through the next link relation. See [Pagination]("
                            "https://developer.okta.com/docs/api/#pagination)."
            ),
        ] = None,
        limit: Annotated[
            Optional[Annotated[int, Field(le=10000, strict=True)]],
            Field(
                description="Specifies the number of group results in a page.  Okta recommends using a specific value other "
                            "than "
                            "the default or maximum. If your request times out, retry your request with a smaller `limit` "
                            "and ["
                            "page the results](https://developer.okta.com/docs/api/#pagination).  The Okta default `Everyone` "
                            "group isn't returned for users with a group admin role."
            ),
        ] = None,
        expand: Annotated[
            Optional[StrictStr],
            Field(
                description="If specified, additional metadata is included in the response. Possible values are `stats` and "
                            "`app`. This additional metadata is listed in the [`_embedded`]("
                            "https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Group/#tag/Group"
                            "/operation/addGroup!c=200&path=_embedded&t=response) property of the response.  > **Note:** You "
                            "can "
                            "use the `stats` value to return the number of users within a group. This is listed as the "
                            "`_embedded.stats.usersCount` value in the response. See this [Knowledge Base article]("
                            "https://support.okta.com/help/s/article/Is-there-an-API-that-returns-the-number-of-users-in-a"
                            "-group"
                            "?language=en_US) for more information and an example."
            ),
        ] = None,
        sort_by: Annotated[
            Optional[StrictStr],
            Field(
                description="Specifies the field to sort by (for search queries only). `sortBy` can be any single property, "
                            "for example `sortBy=profile.name`. Groups with the same value for the `sortBy` property are "
                            "ordered "
                            "by `id`'. Use with `sortOrder` to control the order of results."
            ),
        ] = None,
        sort_order: Annotated[
            Optional[StrictStr],
            Field(
                description="Specifies sort order: `asc` or `desc` (for search queries only). This parameter is ignored if "
                            "`sortBy` isn't present."
            ),
        ] = None,
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> List[Group]:
        """List all groups

        Lists all groups with pagination support.  > **Note:** To list all groups belonging to a member, use the [List all
        groups endpoint in the User Resources API](
        /openapi/okta-management/management/tag/UserResources/#tag/UserResources/operation/listUserGroups).  The number of
        groups returned depends on the specified [`limit`](
        /openapi/okta-management/management/tag/Group/#tag/Group/operation/listGroups!in=query&path=limit&t=request),
        if you have a search, filter, and/or query parameter set, and if that parameter is not null. We recommend using a
        limit less than or equal to 200.  A subset of groups can be returned that match a supported filter expression,
        query, or search criteria.  > **Note:** The `search` parameter results are sourced from an eventually consistent
        datasource and may not reflect the latest information.

        :param search: Searches for groups with a supported [filtering](https://developer.okta.com/docs/api/#filter)
        expression for all properties except for `_embedded`, `_links`, and `objectClass`. Okta recommends this query
        parameter because it provides the largest range of search options and optimal performance.  This operation supports
        [pagination](https://developer.okta.com/docs/api/#pagination).  Using search requires [URL encoding](
        https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding), for example, `search=type eq \"OKTA_GROUP\"` is
        encoded as `search=type+eq+%22OKTA_GROUP%22`.  This operation searches many properties:  * Any group profile
        attribute, including imported app group profile attributes. * The top-level properties: `id`, `created`,
        `lastMembershipUpdated`, `lastUpdated`, and `type`. * The [source](
        /openapi/okta-management/management/tag/Group/#tag/Group/operation/listGroups!c=200&path=_links/source&t=response)
        of groups with type of `APP_GROUP`, accessed as `source.id`.  You can also use the `sortBy` and `sortOrder`
        parameters.  Searches for groups can be filtered by the following operators: `sw`, `eq`, and `co`. You can only use
        `co` with these select profile attributes: `profile.name` and `profile.description`. See [Operators](
        https://developer.okta.com/docs/api/#operators).
        :type search: str
        :param filter: Filter expression for groups. See [Filter](https://developer.okta.com/docs/api/#filter).  Filtering
        supports the following limited number of properties: `id`, `type`, `lastUpdated`, and `lastMembershipUpdated`.  >
        **Note:** All filters must be [URL encoded](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding). For
        example, `filter=lastUpdated gt \"2013-06-01T00:00:00.000Z\"` is encoded as
        `filter=lastUpdated%20gt%20%222013-06-01T00:00:00.000Z%22`.  See [Special characters](
        https://developer.okta.com/docs/api/#special-characters).
        :type filter: str
        :param q: Finds a group that matches the `name` property. > **Note:** Paging and searching are currently mutually
        exclusive. You can't page a query. The default limit for a query is 300 results. Query is intended for an
        auto-complete picker use case where users refine their search string to constrain the results.
        :type q: str
        :param after: Specifies the pagination cursor for the next page of groups. The `after` cursor should be treated as
        an opaque value and obtained through the next link relation. See [Pagination](
        https://developer.okta.com/docs/api/#pagination).
        :type after: str
        :param limit: Specifies the number of group results in a page.  Okta recommends using a specific value other than
        the default or maximum. If your request times out, retry your request with a smaller `limit` and [page the results](
        https://developer.okta.com/docs/api/#pagination).  The Okta default `Everyone` group isn't returned for users with a
        group admin role.
        :type limit: int
        :param expand: If specified, additional metadata is included in the response. Possible values are `stats` and `app`.
        This additional metadata is listed in the [`_embedded`](
        https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Group/#tag/Group/operation/addGroup!c=200
        &path=_embedded&t=response) property of the response.  > **Note:** You can use the `stats` value to return the
        number of users within a group. This is listed as the `_embedded.stats.usersCount` value in the response. See this [
        Knowledge Base article](https://support.okta.com/help/s/article/Is-there-an-API-that-returns-the-number-of-users-in
        -a-group?language=en_US) for more information and an example.
        :type expand: str
        :param sort_by: Specifies the field to sort by (for search queries only). `sortBy` can be any single property,
        for example `sortBy=profile.name`. Groups with the same value for the `sortBy` property are ordered by `id`'. Use
        with `sortOrder` to control the order of results.
        :type sort_by: str
        :param sort_order: Specifies sort order: `asc` or `desc` (for search queries only). This parameter is ignored if
        `sortBy` isn't present.
        :type sort_order: str
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "List[Group]",
            "403": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = self._list_groups_serialize(
            search=search,
            filter=filter,
            q=q,
            after=after,
            limit=limit,
            expand=expand,
            sort_by=sort_by,
            sort_order=sort_order,
            _request_auth=_request_auth,
            _content_type=_content_type,
            _headers=_headers,
            _host_index=_host_index,
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if List[Group] is Success:
                return (None, error)
            else:
                return (None, None, error)

        if List[Group] is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, Group
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if List[Group] is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _list_groups_serialize(
        self,
        search,
        filter,
        q,
        after,
        limit,
        expand,
        sort_by,
        sort_order,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        # process the query parameters
        if search is not None:
            _query_params.append(("search", search))

        if filter is not None:
            _query_params.append(("filter", filter))

        if q is not None:
            _query_params.append(("q", q))

        if after is not None:
            _query_params.append(("after", after))

        if limit is not None:
            _query_params.append(("limit", limit))

        if expand is not None:
            _query_params.append(("expand", expand))

        if sort_by is not None:
            _query_params.append(("sortBy", sort_by))

        if sort_order is not None:
            _query_params.append(("sortOrder", sort_order))

        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="GET",
            resource_path="/api/v1/groups",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def replace_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        group: AddGroupRequest,
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> Group:
        """Replace a group

        Replaces the profile for a group of `OKTA_GROUP` type from your org. > **Note :** You only can modify profiles for
        groups of the `OKTA_GROUP` type. > > App imports are responsible for updating profiles for groups of the `APP_GROUP`
        type, such as Active Directory groups.

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param group: (required)
        :type group: AddGroupRequest
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "200": "Group",
            "400": "Error",
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = self._replace_group_serialize(
            group_id=group_id,
            group=group,
            _request_auth=_request_auth,
            _content_type=_content_type,
            _headers=_headers,
            _host_index=_host_index,
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            if Group is Success:
                return (None, error)
            else:
                return (None, None, error)

        if Group is Success:
            response, response_body, error = await self._request_executor.execute(
                request
            )
        else:
            response, response_body, error = await self._request_executor.execute(
                request, Group
            )

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                if Group is Success:
                    return (response, error)
                else:
                    return (None, response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _replace_group_serialize(
        self,
        group_id,
        group,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter
        if group is not None:
            _body_params = group

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # set the HTTP header `Content-Type`
        if _content_type:
            _header_params["Content-Type"] = _content_type
        else:
            _default_content_type = self.select_header_content_type(
                ["application/json"]
            )
            if _default_content_type is not None:
                _header_params["Content-Type"] = _default_content_type

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="PUT",
            resource_path="/api/v1/groups/{groupId}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )

    @validate_call
    async def unassign_user_from_group(
        self,
        group_id: Annotated[StrictStr, Field(description="The `id` of the group")],
        user_id: Annotated[StrictStr, Field(description="ID of an existing Okta user")],
        _request_timeout: Union[
            None,
            Annotated[StrictFloat, Field(gt=0)],
            Tuple[
                Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]
            ],
        ] = None,
        _request_auth: Optional[Dict[StrictStr, Any]] = None,
        _content_type: Optional[StrictStr] = None,
        _headers: Optional[Dict[StrictStr, Any]] = None,
        _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
    ) -> None:
        """Unassign a user from a group

        Unassigns a user from a group with the `OKTA_GROUP` type. > **Note:** You only can modify memberships for groups of
        the `OKTA_GROUP` type. > > App imports are responsible for managing group memberships for groups of the `APP_GROUP`
        type, such as Active Directory groups.

        :param group_id: The `id` of the group (required)
        :type group_id: str
        :param user_id: ID of an existing Okta user (required)
        :type user_id: str
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :type _request_timeout: int, tuple(int, int), optional
        :param _request_auth: set to override the auth_settings for an a single
                              request; this effectively ignores the
                              authentication in the spec for a single request.
        :type _request_auth: dict, optional
        :param _content_type: force content-type for the request.
        :type _content_type: str, Optional
        :param _headers: set to override the headers for a single
                         request; this effectively ignores the headers
                         in the spec for a single request.
        :type _headers: dict, optional
        :param _host_index: set to override the host_index for a single
                            request; this effectively ignores the host_index
                            in the spec for a single request.
        :type _host_index: int, optional
        :return: Returns the result object.
        """  # noqa: E501

        _response_types_map: Dict[str, Optional[str]] = {
            "204": None,
            "403": "Error",
            "404": "Error",
            "429": "Error",
        }

        method, url, header_params, body, post_params = (
            self._unassign_user_from_group_serialize(
                group_id=group_id,
                user_id=user_id,
                _request_auth=_request_auth,
                _content_type=_content_type,
                _headers=_headers,
                _host_index=_host_index,
            )
        )

        form = {}
        keep_empty_params = False

        request, error = await self._request_executor.create_request(
            method, url, body, header_params, form, keep_empty_params=keep_empty_params
        )

        if error:
            return (None, error)

        response, response_body, error = await self._request_executor.execute(request)

        if response_body == "" or response.status == 204:
            response_data = RESTResponse(response)
            resp = ApiResponse(
                status_code=response_data.status,
                data=None,
                headers=response_data.getheaders(),
                raw_data=b"",
            )
            return (None, resp, None)
        else:
            response_body = response_body.encode("utf-8")

            if error:
                return (response, error)

            response_data = RESTResponse(response)
            response_data.read(response_body)
            resp = self.response_deserialize(
                response_data=response_data,
                response_types_map=_response_types_map,
            )
            return (resp.data, resp, None)

    def _unassign_user_from_group_serialize(
        self,
        group_id,
        user_id,
        _request_auth,
        _content_type,
        _headers,
        _host_index,
    ) -> RequestSerialized:

        _host = None

        _collection_formats: Dict[str, str] = {}

        _path_params: Dict[str, str] = {}
        _query_params: List[Tuple[str, str]] = []
        _header_params: Dict[str, Optional[str]] = _headers or {}
        _form_params: List[Tuple[str, str]] = []
        _files: Dict[str, Union[str, bytes]] = {}
        _body_params: Optional[bytes] = None

        # process the path parameters
        if group_id is not None:
            _path_params["groupId"] = group_id
        if user_id is not None:
            _path_params["userId"] = user_id
        # process the query parameters
        # process the header parameters
        # process the form parameters
        # process the body parameter

        # set the HTTP header `Accept`
        _header_params["Accept"] = self.select_header_accept(["application/json"])

        # authentication setting
        _auth_settings: List[str] = ["apiToken", "oauth2"]

        return self.param_serialize(
            method="DELETE",
            resource_path="/api/v1/groups/{groupId}/users/{userId}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            body=_body_params,
            post_params=_form_params,
            files=_files,
            auth_settings=_auth_settings,
            collection_formats=_collection_formats,
            _host=_host,
            _request_auth=_request_auth,
        )
