Skip to content

Commit

Permalink
String instead of Enum in project management API
Browse files Browse the repository at this point in the history
  • Loading branch information
Raalsky authored Oct 19, 2021
2 parents afe9ec9 + 641004b commit a318c37
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 17 deletions.
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

0 comments on commit a318c37

Please sign in to comment.