Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

String instead of Enum in project management API #738

Merged
merged 10 commits into from
Oct 19, 2021
Merged
5 changes: 5 additions & 0 deletions neptune/management/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ class ProjectsLimitReached(ManagementOperationFailure):
description = 'Project number limit reached.'


class UnsupportedValue(ManagementOperationFailure):
code = 12
description = '{enum} cannot have value {value}'


class BadRequestException(ManagementOperationFailure):
code = 400
description = 'Your request has encountered following validation errors: {validation_errors}'
174 changes: 166 additions & 8 deletions neptune/management/internal/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
BadRequestException,
ProjectsLimitReached,
)
from neptune.management.internal.dto import ProjectVisibilityDTO, ProjectMemberRoleDTO, WorkspaceMemberRoleDTO


def _get_token(api_token: Optional[str] = None) -> str:
Expand All @@ -64,6 +65,22 @@ def _get_backend_client(api_token: Optional[str] = None) -> SwaggerClient:

@with_api_exceptions_handler
def get_project_list(api_token: Optional[str] = None) -> List[str]:
"""Get a list of projects you have access to.
Args:
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Returns:
``List[str]``: list of project names of a format 'WORKSPACE/PROJECT'
Examples:
>>> from neptune import management
>>> management.get_project_list()
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('api_token', api_token, (str, type(None)))

backend_client = _get_backend_client(api_token=api_token)
Expand All @@ -84,14 +101,46 @@ def create_project(
name: str,
key: str,
workspace: Optional[str] = None,
visibility: ProjectVisibility = ProjectVisibility.PRIVATE,
visibility: str = ProjectVisibility.PRIVATE,
description: Optional[str] = None,
api_token: Optional[str] = None
) -> str:
"""Creates a new project in your Neptune workspace.
Args:
name(str): The name of the project in Neptune in the format 'WORKSPACE/PROJECT'.
If workspace argument was set, it should only contain 'PROJECT' instead of 'WORKSPACE/PROJECT'.
key(str): Project identifier. It has to be contain 1-10 upper case letters or numbers.
For example, 'GOOD5'
workspace(str, optional): Name of your Neptune workspace.
If you specify it, change the format of the name argument to 'PROJECT' instead of 'WORKSPACE/PROJECT'.
If 'None' it will be parsed from the `name` argument.
visibility(str, optional): level of visibility you want your project to have.
Can be set to:
- 'pub' for public projects
- 'priv' for private projects
If 'None' it will be set to 'priv'
description(str, optional): Project description.
If 'None', it will be left empty.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Returns:
``str``: name of the new project you created.
Examples:
>>> from neptune import management
>>> management.create_project(name="awesome-team/amazing-project",
... key="AMA",
... visibility="pub")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('name', name, str)
verify_type('key', key, str)
verify_type('workspace', workspace, (str, type(None)))
verify_type('visibility', visibility, ProjectVisibility)
verify_type('visibility', visibility, str)
verify_type('description', description, (str, type(None)))
verify_type('api_token', api_token, (str, type(None)))

Expand All @@ -116,7 +165,7 @@ def create_project(
'description': description,
'projectKey': key,
'organizationId': workspace_name_to_id[workspace],
'visibility': visibility.value
'visibility': ProjectVisibilityDTO.from_str(visibility).value
},
**DEFAULT_REQUEST_KWARGS
}
Expand All @@ -135,6 +184,25 @@ def create_project(

@with_api_exceptions_handler
def delete_project(name: str, workspace: Optional[str] = None, api_token: Optional[str] = None):
"""Deletes a project from your Neptune workspace.
Args:
name(str): The name of the project in Neptune in the format 'WORKSPACE/PROJECT'.
If workspace argument was set, it should only contain 'PROJECT' instead of 'WORKSPACE/PROJECT'.
workspace(str, optional): Name of your Neptune workspace.
If you specify it, change the format of the name argument to 'PROJECT' instead of 'WORKSPACE/PROJECT'.
If 'None' it will be parsed from the name argument.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Examples:
>>> from neptune import management
>>> management.delete_project(name="awesome-team/amazing-project")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('name', name, str)
verify_type('workspace', workspace, (str, type(None)))
verify_type('api_token', api_token, (str, type(None)))
Expand All @@ -159,13 +227,43 @@ def delete_project(name: str, workspace: Optional[str] = None, api_token: Option
def add_project_member(
name: str,
username: str,
role: MemberRole,
role: str,
workspace: Optional[str] = None,
api_token: Optional[str] = None
):
"""Adds member to the Neptune project.
Args:
name(str): The name of the project in Neptune in the format 'WORKSPACE/PROJECT'.
If workspace argument was set, it should only contain 'PROJECT' instead of 'WORKSPACE/PROJECT'.
username(str): Name of the user you want to add to the project.
role(str): level of permissions the user should have in a project.
Can be set to:
- 'viewer': can only view project content and members
- 'contributor': can view and edit project content and only view members
- 'owner': can view and edit project content and members
For more information, see `user roles in a project docs`_.
workspace(str, optional): Name of your Neptune workspace.
If you specify it, change the format of the name argument to 'PROJECT' instead of 'WORKSPACE/PROJECT'.
If 'None' it will be parsed from the name argument.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Examples:
>>> from neptune import management
>>> management.add_project_member(name="awesome-team/amazing-project",
... username="johny",
... role="contributor")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
.. _user roles in a project docs:
https://docs.neptune.ai/administration/user-management#roles-in-a-project
"""
verify_type('name', name, str)
verify_type('username', username, str)
verify_type('role', role, MemberRole)
verify_type('role', role, str)
verify_type('workspace', workspace, (str, type(None)))
verify_type('api_token', api_token, (str, type(None)))

Expand All @@ -176,7 +274,7 @@ def add_project_member(
'projectIdentifier': project_identifier,
'member': {
'userId': username,
'role': role.value
'role': ProjectMemberRoleDTO.from_str(role).value
},
**DEFAULT_REQUEST_KWARGS
}
Expand All @@ -195,6 +293,28 @@ def get_project_member_list(
workspace: Optional[str] = None,
api_token: Optional[str] = None
) -> Dict[str, str]:
"""Get a list of members for a project.
Args:
name(str): The name of the project in Neptune in the format 'WORKSPACE/PROJECT'.
If workspace argument was set it should only contain 'PROJECT' instead of 'WORKSPACE/PROJECT'.
workspace(str, optional): Name of your Neptune workspace.
If you specify change the format of the name argument to 'PROJECT' instead of 'WORKSPACE/PROJECT'.
If 'None' it will be parsed from the name argument.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Returns:
``Dict[str, str]``: Dictionary with usernames as keys and ProjectMemberRoles
('owner', 'contributor', 'viewer') as values.
Examples:
>>> from neptune import management
>>> management.get_project_member_list(name="awesome-team/amazing-project")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('name', name, str)
verify_type('workspace', workspace, (str, type(None)))
verify_type('api_token', api_token, (str, type(None)))
Expand All @@ -209,7 +329,7 @@ def get_project_member_list(

try:
result = backend_client.api.listProjectMembers(**params).response().result
return {f'{m.registeredMemberInfo.username}': m.role for m in result}
return {f'{m.registeredMemberInfo.username}': ProjectMemberRoleDTO.to_domain(m.role) for m in result}
except HTTPNotFound as e:
raise ProjectNotFound(name=project_identifier) from e

Expand All @@ -221,6 +341,27 @@ def remove_project_member(
workspace: Optional[str] = None,
api_token: Optional[str] = None
):
"""Removes member from the Neptune project.
Args:
name(str): The name of the project in Neptune in the format 'WORKSPACE/PROJECT'.
If workspace argument was set, it should only contain 'PROJECT' instead of 'WORKSPACE/PROJECT'.
username(str): name of the user you want to remove from the project.
workspace(str, optional): Name of your Neptune workspace.
If you specify change the format of the name argument to 'PROJECT' instead of 'WORKSPACE/PROJECT'.
If 'None' it will be parsed from the name argument.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Examples:
>>> from neptune import management
>>> management.remove_project_member(name="awesome-team/amazing-project",
... username="johny")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('name', name, str)
verify_type('username', username, str)
verify_type('workspace', workspace, (str, type(None)))
Expand All @@ -247,6 +388,23 @@ def remove_project_member(

@with_api_exceptions_handler
def get_workspace_member_list(name: str, api_token: Optional[str] = None) -> Dict[str, str]:
"""Get a list of members of a workspace.
Args:
name(str, optional): Name of your Neptune workspace.
api_token(str, optional): User’s API token. Defaults to `None`.
If `None`, the value of `NEPTUNE_API_TOKEN` environment variable will be taken.
.. note::
It is strongly recommended to use `NEPTUNE_API_TOKEN` environment variable rather than placing your
API token in plain text in your source code.
Returns:
``Dict[str, str]``: Dictionary with usernames as keys and `WorkspaceMemberRole` ('member', 'admin') as values.
Examples:
>>> from neptune import management
>>> management.get_workspace_member_list(name="awesome-team")
You may also want to check `management API reference`_.
.. _management API reference:
https://docs.neptune.ai/api-reference/management
"""
verify_type('name', name, str)
verify_type('api_token', api_token, (str, type(None)))

Expand All @@ -259,6 +417,6 @@ def get_workspace_member_list(name: str, api_token: Optional[str] = None) -> Dic

try:
result = backend_client.api.listOrganizationMembers(**params).response().result
return {f'{m.registeredMemberInfo.username}': m.role for m in result}
return {f'{m.registeredMemberInfo.username}': WorkspaceMemberRoleDTO.to_domain(m.role) for m in result}
except HTTPNotFound as e:
raise WorkspaceNotFound(workspace=name) from e
79 changes: 79 additions & 0 deletions neptune/management/internal/dto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#
# Copyright (c) 2021, Neptune Labs Sp. z o.o.
#
# 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.
#
from enum import Enum

from neptune.new.internal.utils import verify_type

from neptune.management.exceptions import UnsupportedValue
from neptune.management.internal.types import ProjectVisibility, ProjectMemberRole, WorkspaceMemberRole


class ProjectVisibilityDTO(Enum):
PRIVATE = 'priv'
PUBLIC = 'pub'

@classmethod
def from_str(cls, visibility: str) -> 'ProjectVisibilityDTO':
verify_type('visibility', visibility, str)

try:
return {
ProjectVisibility.PRIVATE: ProjectVisibilityDTO.PRIVATE,
ProjectVisibility.PUBLIC: ProjectVisibilityDTO.PUBLIC
}[visibility]
except KeyError as e:
raise UnsupportedValue(enum=cls.__name__, value=visibility) from e


class ProjectMemberRoleDTO(Enum):
VIEWER = 'viewer'
MEMBER = 'member'
MANAGER = 'manager'

@classmethod
def from_str(cls, role: str) -> 'ProjectMemberRoleDTO':
verify_type('role', role, str)

try:
return {
ProjectMemberRole.VIEWER: ProjectMemberRoleDTO.VIEWER,
ProjectMemberRole.CONTRIBUTOR: ProjectMemberRoleDTO.MEMBER,
ProjectMemberRole.OWNER: ProjectMemberRoleDTO.MANAGER
}[role]
except KeyError as e:
raise UnsupportedValue(enum=cls.__name__, value=role) from e

@staticmethod
def to_domain(role: str) -> str:
verify_type('role', role, str)

return {
ProjectMemberRoleDTO.VIEWER.value: ProjectMemberRole.VIEWER,
ProjectMemberRoleDTO.MANAGER.value: ProjectMemberRole.OWNER,
ProjectMemberRoleDTO.MEMBER.value: ProjectMemberRole.CONTRIBUTOR
}.get(role)


class WorkspaceMemberRoleDTO(Enum):
OWNER = 'owner'
MEMBER = 'member'

@staticmethod
def to_domain(role: str) -> str:
return {
WorkspaceMemberRoleDTO.OWNER.value: WorkspaceMemberRole.ADMIN,
WorkspaceMemberRoleDTO.MEMBER.value: WorkspaceMemberRole.MEMBER
}.get(role)
21 changes: 15 additions & 6 deletions neptune/management/internal/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from enum import Enum


class ProjectVisibility(Enum):
class ProjectVisibility:
PRIVATE = 'priv'
PUBLIC = 'pub'


class MemberRole(Enum):
class ProjectMemberRole:
VIEWER = 'viewer'
OWNER = 'owner'
CONTRIBUTOR = 'contributor'

# Deprecated
MEMBER = CONTRIBUTOR
MANAGER = OWNER


MemberRole = ProjectMemberRole


class WorkspaceMemberRole:
ADMIN = 'admin'
MEMBER = 'member'
MANAGER = 'manager'
Loading