Projects¶
The projects feature allow for collaboration between different users across the entire UCloud platform.
Rationale¶
This document establishes the core abstractions for projects and establishes an event stream for receiving updates about changes. Other services extend the projects feature and subscribe to these changes to create the full project feature.
Definition¶
A project in UCloud is a collection of members
which is uniquely identified by an id
. All members
are
users identified by their username
with each having
exactly one role
. Each project has exactly one principal investigator (PI
), and can have multiple ADMIN
s and USER
s.
Role | Notes |
---|---|
PI |
The primary point of contact for projects. Responsible for managing the project, including adding and removing users. |
ADMIN |
Administrators are allowed to perform some project management. |
USER |
Has no special privileges. |
Table: The possible roles of a project, and their privileges within project management.
📝 NOTE: PI within a project acts on behalf of a research institution and enforces the rules and procedures. The PI can invite an Admin user with whom he/she shares responsibilities.
A project can be updated by adding/removing/changing any of its members
.
A project is sub-divided into groups:
Each project may have 0 or more groups. The groups can have 0 or more members. A group belongs to exactly one project, and the members of a group can only be from the project it belongs to.
Special Groups¶
All projects have some special groups. The most common, and as of 06/06/24 the only, special group is the “All Users” group. This group automatically contains all members of the project. These are synchronized every single time a user is added or removed from a project. This special group is used by providers when registering resources with UCloud.
Creating Projects and Sub-Projects¶
All projects created by end-users have exactly one parent project. Only UCloud administrators can create root-level projects, that is a project without a parent. This allows users of UCloud to create a hierarchy of projects. The project hierarchy plays a significant role in accounting.
Normal users can create a project through the grant application feature.
A project can be uniquely identified by the path from the root project to the leaf-project. As a result, the title
of
a subproject must be unique within a single project. title
s are case-insensitive.
Permissions and memberships are not hierarchical. This means that a user must be explicitly added to every project they need permissions in. UCloud administrators can always create a sub-project in any given project. A setting exists for every project which allows normal users to create sub-projects.
Example: A project hierarchy
Figure 1: A project hierarchy
Figure 1 shows a hierarchy of projects. Note that users deep in the hierarchy are not necessarily members of the projects further up in the hierarchy. For example, being a member of “IMADA” does not imply membership of “NAT”. A member of “IMADA” can be a member of “NAT” but they must be explicitly added to both projects.
None of the projects share any resources. Each individual project will have their own home directory. The administrators, or any other user, of “NAT” will not be able to read/write any files of “IMADA” unless they have explicitly been added to the “IMADA” project.
The Project Context (also known as workspace)¶
All requests in UCloud are executed in a particular context. The header of every request defines the context. For the
HTTP backend this is done in the Project
header. The absence of a project implies that the request is executed in the
personal project context.
Figure 2: The UCloud user interface allows you to select context through a dropdown in the navigation header.
Example: Accessing the project context from a service
implement(Descriptions.call) {
val project: String? = ctx.project // null implies the personal project
ok(service.doSomething(project))
}
Table of Contents¶
1. Remote Procedure Calls
Name | Description |
---|---|
browse |
No description |
browseInviteLinks |
No description |
browseInvites |
No description |
retrieve |
No description |
retrieveGroup |
No description |
retrieveInviteLinkProject |
No description |
retrieveProviderProject |
No description |
retrieveProviderProjectInternal |
No description |
acceptInvite |
No description |
acceptInviteLink |
No description |
archive |
No description |
changeRole |
No description |
create |
No description |
createGroup |
No description |
createGroupMember |
No description |
createInvite |
No description |
createInviteLink |
No description |
deleteGroup |
No description |
deleteGroupMember |
No description |
deleteInvite |
No description |
deleteInviteLink |
No description |
deleteMember |
No description |
projectVerificationStatus |
No description |
renameGroup |
No description |
renameProject |
No description |
retrieveAllUsersGroup |
No description |
toggleFavorite |
No description |
unarchive |
No description |
updateInviteLink |
No description |
updateSettings |
No description |
verifyMembership |
No description |
2. Data Models
Remote Procedure Calls¶
browse
¶
Request | Response | Error |
---|---|---|
ProjectsBrowseRequest |
PageV2<Project> |
CommonErrorMessage |
browseInviteLinks
¶
Request | Response | Error |
---|---|---|
ProjectsBrowseInviteLinksRequest |
PageV2<ProjectInviteLink> |
CommonErrorMessage |
browseInvites
¶
Request | Response | Error |
---|---|---|
ProjectsBrowseInvitesRequest |
PageV2<ProjectInvite> |
CommonErrorMessage |
retrieve
¶
Request | Response | Error |
---|---|---|
ProjectsRetrieveRequest |
Project |
CommonErrorMessage |
retrieveGroup
¶
Request | Response | Error |
---|---|---|
ProjectsRetrieveGroupRequest |
Group |
CommonErrorMessage |
retrieveInviteLinkProject
¶
Request | Response | Error |
---|---|---|
ProjectsRetrieveInviteLinkInfoRequest |
ProjectsRetrieveInviteLinkInfoResponse |
CommonErrorMessage |
retrieveProviderProject
¶
Request | Response | Error |
---|---|---|
Unit |
Project |
CommonErrorMessage |
retrieveProviderProjectInternal
¶
Request | Response | Error |
---|---|---|
FindByStringId |
FindByStringId |
CommonErrorMessage |
acceptInvite
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByProjectId> |
Unit |
CommonErrorMessage |
acceptInviteLink
¶
Request | Response | Error |
---|---|---|
ProjectsAcceptInviteLinkRequest |
ProjectsAcceptInviteLinkResponse |
CommonErrorMessage |
archive
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByStringId> |
Unit |
CommonErrorMessage |
changeRole
¶
Request | Response | Error |
---|---|---|
BulkRequest<ProjectsChangeRoleRequestItem> |
Unit |
CommonErrorMessage |
create
¶
Request | Response | Error |
---|---|---|
BulkRequest<Project.Specification> |
BulkResponse<FindByStringId> |
CommonErrorMessage |
createGroup
¶
Request | Response | Error |
---|---|---|
BulkRequest<Group.Specification> |
BulkResponse<FindByStringId> |
CommonErrorMessage |
createGroupMember
¶
Request | Response | Error |
---|---|---|
BulkRequest<GroupMember> |
Unit |
CommonErrorMessage |
createInvite
¶
Request | Response | Error |
---|---|---|
BulkRequest<ProjectsCreateInviteRequestItem> |
Unit |
CommonErrorMessage |
createInviteLink
¶
Request | Response | Error |
---|---|---|
Unit |
ProjectInviteLink |
CommonErrorMessage |
deleteGroup
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByStringId> |
Unit |
CommonErrorMessage |
deleteGroupMember
¶
Request | Response | Error |
---|---|---|
BulkRequest<GroupMember> |
Unit |
CommonErrorMessage |
deleteInvite
¶
Request | Response | Error |
---|---|---|
BulkRequest<ProjectsDeleteInviteRequestItem> |
Unit |
CommonErrorMessage |
deleteInviteLink
¶
Request | Response | Error |
---|---|---|
ProjectsDeleteInviteLinkRequest |
Unit |
CommonErrorMessage |
deleteMember
¶
Request | Response | Error |
---|---|---|
BulkRequest<ProjectsDeleteMemberRequestItem> |
Unit |
CommonErrorMessage |
projectVerificationStatus
¶
Request | Response | Error |
---|---|---|
BulkRequest<SetProjectVerificationStatusRequest> |
Unit |
CommonErrorMessage |
renameGroup
¶
Request | Response | Error |
---|---|---|
BulkRequest<ProjectsRenameGroupRequestItem> |
Unit |
CommonErrorMessage |
renameProject
¶
Request | Response | Error |
---|---|---|
BulkRequest<RenameProjectRequest> |
Unit |
CommonErrorMessage |
retrieveAllUsersGroup
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByProjectId> |
BulkResponse<FindByStringId> |
CommonErrorMessage |
toggleFavorite
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByStringId> |
Unit |
CommonErrorMessage |
unarchive
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByStringId> |
Unit |
CommonErrorMessage |
updateInviteLink
¶
Request | Response | Error |
---|---|---|
ProjectsUpdateInviteLinkRequest |
Unit |
CommonErrorMessage |
updateSettings
¶
Request | Response | Error |
---|---|---|
Project.Settings |
Unit |
CommonErrorMessage |
verifyMembership
¶
Request | Response | Error |
---|---|---|
BulkRequest<FindByStringId> |
Unit |
CommonErrorMessage |
Data Models¶
FindByProjectId
¶
data class FindByProjectId(
val project: String,
)
Properties
project
: String
String
Group
¶
data class Group(
val id: String,
val createdAt: Long,
val specification: Group.Specification,
val status: Group.Status,
)
Group.Specification
¶
data class Specification(
val project: String,
val title: String,
)
Group.Status
¶
data class Status(
val members: List<String>?,
)
GroupMember
¶
data class GroupMember(
val username: String,
val group: String,
)
Project
¶
data class Project(
val id: String,
val createdAt: Long,
val specification: Project.Specification,
val modifiedAt: Long,
val status: Project.Status,
)
Properties
id
: String
String
createdAt
: Long
Long
specification
: Project.Specification
Project.Specification
modifiedAt
: Long
Long
status
: Project.Status
Project.Status
Project.Settings
¶
data class Settings(
val subprojects: Project.Settings.SubProjects?,
)
Properties
subprojects
: Project.Settings.SubProjects?
Project.Settings.SubProjects?
Project.Settings.SubProjects
¶
data class SubProjects(
val allowRenaming: Boolean?,
)
Properties
allowRenaming
: Boolean?
Boolean?
Project.Specification
¶
data class Specification(
val parent: String?,
val title: String,
val canConsumeResources: Boolean?,
)
Project.Status
¶
data class Status(
val archived: Boolean,
val isFavorite: Boolean?,
val members: List<ProjectMember>?,
val groups: List<Group>?,
val settings: Project.Settings?,
val myRole: ProjectRole?,
val path: String?,
)
Properties
archived
: Boolean
A flag which indicates if the project is currently archived.
Boolean
Currently, archiving does not mean a lot in UCloud. This is subject to change in the future. For the most
part, archived projects simply do not appear when using a browse
, unless includeArchived = true
.
isFavorite
: Boolean?
A flag which indicates if the current user has marked this as one of their favorite projects.
Boolean?
members
: List<ProjectMember>?
A list of project members, conditionally included if `includeMembers = true`.
List<ProjectMember>?
NOTE: This list will contain all members of a project, always. There are currently no plans for a pagination API. This might change in the future if it becomes plausible that projects have many thousands of members.
groups
: List<Group>?
A list of groups, conditionally included if `includeGroups = true`.
List<Group>?
NOTE: This list will contain all groups of a project, always. There are currently no plans for a pagination API. This might change in the future if it becomes plausible that projects have many thousands of groups.
settings
: Project.Settings?
The settings of this project, conditionally included if `includeSettings = true`.
Project.Settings?
myRole
: ProjectRole?
The role of the current user, this value is always included.
ProjectRole?
This is typically not-null, but it can be null if the request was made by an actor which has access to the
project without being a member. Common examples include: Actor.System
and a relevant provider.
path
: String?
A path to this project, conditionally included if `includePath = true`.
String?
The path is a ‘/’ separated string where each component is a project title. The path will not contain this project. The path does not start or end with a ‘/’. If the project is a root, then “” will be returned.
ProjectInvite
¶
data class ProjectInvite(
val createdAt: Long,
val invitedBy: String,
val invitedTo: String,
val recipient: String,
val projectTitle: String,
)
ProjectInviteLink
¶
data class ProjectInviteLink(
val token: String,
val expires: Long,
val groupAssignment: List<String>?,
val roleAssignment: ProjectRole?,
)
ProjectInviteType
¶
enum class ProjectInviteType {
INGOING,
OUTGOING,
}
Properties
INGOING
OUTGOING
ProjectMember
¶
data class ProjectMember(
val username: String,
val role: ProjectRole,
val email: String?,
)
ProjectsSortBy
¶
enum class ProjectsSortBy {
favorite,
title,
parent,
}
Properties
favorite
title
parent
ProjectsAcceptInviteLinkRequest
¶
data class ProjectsAcceptInviteLinkRequest(
val token: String,
)
Properties
token
: String
String
ProjectsBrowseInviteLinksRequest
¶
The base type for requesting paginated content.
data class ProjectsBrowseInviteLinksRequest(
val itemsPerPage: Int?,
val next: String?,
val consistency: PaginationRequestV2Consistency?,
val itemsToSkip: Long?,
)
Paginated content can be requested with one of the following consistency
guarantees, this greatly changes the
semantics of the call:
Consistency | Description |
---|---|
PREFER |
Consistency is preferred but not required. An inconsistent snapshot might be returned. |
REQUIRE |
Consistency is required. A request will fail if consistency is no longer guaranteed. |
The consistency
refers to if collecting all the results via the pagination API are consistent. We consider the
results to be consistent if it contains a complete view at some point in time. In practice this means that the results
must contain all the items, in the correct order and without duplicates.
If you use the PREFER
consistency then you may receive in-complete results that might appear out-of-order and can
contain duplicate items. UCloud will still attempt to serve a snapshot which appears mostly consistent. This is helpful
for user-interfaces which do not strictly depend on consistency but would still prefer something which is mostly
consistent.
The results might become inconsistent if the client either takes too long, or a service instance goes down while
fetching the results. UCloud attempts to keep each next
token alive for at least one minute before invalidating it.
This does not mean that a client must collect all results within a minute but rather that they must fetch the next page
within a minute of the last page. If this is not feasible and consistency is not required then PREFER
should be used.
📝 NOTE: Services are allowed to ignore extra criteria of the request if the next
token is supplied. This is
needed in order to provide a consistent view of the results. Clients should provide the same criterion as they
paginate through the results.
Properties
itemsPerPage
: Int?
Requested number of items per page. Supported values: 10, 25, 50, 100, 250.
Int?
next
: String?
A token requesting the next page of items
String?
consistency
: PaginationRequestV2Consistency?
Controls the consistency guarantees provided by the backend
PaginationRequestV2Consistency?
itemsToSkip
: Long?
Items to skip ahead
Long?
ProjectsBrowseInvitesRequest
¶
The base type for requesting paginated content.
data class ProjectsBrowseInvitesRequest(
val itemsPerPage: Int?,
val next: String?,
val consistency: PaginationRequestV2Consistency?,
val itemsToSkip: Long?,
val filterType: ProjectInviteType?,
)
Paginated content can be requested with one of the following consistency
guarantees, this greatly changes the
semantics of the call:
Consistency | Description |
---|---|
PREFER |
Consistency is preferred but not required. An inconsistent snapshot might be returned. |
REQUIRE |
Consistency is required. A request will fail if consistency is no longer guaranteed. |
The consistency
refers to if collecting all the results via the pagination API are consistent. We consider the
results to be consistent if it contains a complete view at some point in time. In practice this means that the results
must contain all the items, in the correct order and without duplicates.
If you use the PREFER
consistency then you may receive in-complete results that might appear out-of-order and can
contain duplicate items. UCloud will still attempt to serve a snapshot which appears mostly consistent. This is helpful
for user-interfaces which do not strictly depend on consistency but would still prefer something which is mostly
consistent.
The results might become inconsistent if the client either takes too long, or a service instance goes down while
fetching the results. UCloud attempts to keep each next
token alive for at least one minute before invalidating it.
This does not mean that a client must collect all results within a minute but rather that they must fetch the next page
within a minute of the last page. If this is not feasible and consistency is not required then PREFER
should be used.
📝 NOTE: Services are allowed to ignore extra criteria of the request if the next
token is supplied. This is
needed in order to provide a consistent view of the results. Clients should provide the same criterion as they
paginate through the results.
Properties
itemsPerPage
: Int?
Requested number of items per page. Supported values: 10, 25, 50, 100, 250.
Int?
next
: String?
A token requesting the next page of items
String?
consistency
: PaginationRequestV2Consistency?
Controls the consistency guarantees provided by the backend
PaginationRequestV2Consistency?
itemsToSkip
: Long?
Items to skip ahead
Long?
filterType
: ProjectInviteType?
ProjectInviteType?
ProjectsBrowseRequest
¶
The base type for requesting paginated content.
data class ProjectsBrowseRequest(
val itemsPerPage: Int?,
val next: String?,
val consistency: PaginationRequestV2Consistency?,
val itemsToSkip: Long?,
val includeMembers: Boolean?,
val includeGroups: Boolean?,
val includeFavorite: Boolean?,
val includeArchived: Boolean?,
val includeSettings: Boolean?,
val includePath: Boolean?,
val sortBy: ProjectsSortBy?,
val sortDirection: SortDirection?,
)
Paginated content can be requested with one of the following consistency
guarantees, this greatly changes the
semantics of the call:
Consistency | Description |
---|---|
PREFER |
Consistency is preferred but not required. An inconsistent snapshot might be returned. |
REQUIRE |
Consistency is required. A request will fail if consistency is no longer guaranteed. |
The consistency
refers to if collecting all the results via the pagination API are consistent. We consider the
results to be consistent if it contains a complete view at some point in time. In practice this means that the results
must contain all the items, in the correct order and without duplicates.
If you use the PREFER
consistency then you may receive in-complete results that might appear out-of-order and can
contain duplicate items. UCloud will still attempt to serve a snapshot which appears mostly consistent. This is helpful
for user-interfaces which do not strictly depend on consistency but would still prefer something which is mostly
consistent.
The results might become inconsistent if the client either takes too long, or a service instance goes down while
fetching the results. UCloud attempts to keep each next
token alive for at least one minute before invalidating it.
This does not mean that a client must collect all results within a minute but rather that they must fetch the next page
within a minute of the last page. If this is not feasible and consistency is not required then PREFER
should be used.
📝 NOTE: Services are allowed to ignore extra criteria of the request if the next
token is supplied. This is
needed in order to provide a consistent view of the results. Clients should provide the same criterion as they
paginate through the results.
Properties
itemsPerPage
: Int?
Requested number of items per page. Supported values: 10, 25, 50, 100, 250.
Int?
next
: String?
A token requesting the next page of items
String?
consistency
: PaginationRequestV2Consistency?
Controls the consistency guarantees provided by the backend
PaginationRequestV2Consistency?
itemsToSkip
: Long?
Items to skip ahead
Long?
includeMembers
: Boolean?
Boolean?
includeGroups
: Boolean?
Boolean?
includeFavorite
: Boolean?
Boolean?
includeArchived
: Boolean?
Boolean?
includeSettings
: Boolean?
Boolean?
includePath
: Boolean?
Boolean?
sortBy
: ProjectsSortBy?
ProjectsSortBy?
sortDirection
: SortDirection?
SortDirection?
ProjectsChangeRoleRequestItem
¶
data class ProjectsChangeRoleRequestItem(
val username: String,
val role: ProjectRole,
)
ProjectsCreateInviteRequestItem
¶
data class ProjectsCreateInviteRequestItem(
val recipient: String,
)
Properties
recipient
: String
String
ProjectsDeleteInviteLinkRequest
¶
data class ProjectsDeleteInviteLinkRequest(
val token: String,
)
Properties
token
: String
String
ProjectsDeleteInviteRequestItem
¶
data class ProjectsDeleteInviteRequestItem(
val project: String,
val username: String,
)
ProjectsDeleteMemberRequestItem
¶
data class ProjectsDeleteMemberRequestItem(
val username: String,
)
Properties
username
: String
String
ProjectsRenameGroupRequestItem
¶
data class ProjectsRenameGroupRequestItem(
val group: String,
val newTitle: String,
)
ProjectsRetrieveGroupRequest
¶
data class ProjectsRetrieveGroupRequest(
val id: String,
val includeMembers: Boolean?,
)
ProjectsRetrieveInviteLinkInfoRequest
¶
data class ProjectsRetrieveInviteLinkInfoRequest(
val token: String,
)
Properties
token
: String
String
ProjectsRetrieveRequest
¶
data class ProjectsRetrieveRequest(
val id: String,
val includeMembers: Boolean?,
val includeGroups: Boolean?,
val includeFavorite: Boolean?,
val includeArchived: Boolean?,
val includeSettings: Boolean?,
val includePath: Boolean?,
)
ProjectsUpdateInviteLinkRequest
¶
data class ProjectsUpdateInviteLinkRequest(
val token: String,
val role: ProjectRole,
val groups: List<String>,
)
RenameProjectRequest
¶
data class RenameProjectRequest(
val id: String,
val newTitle: String,
)
SetProjectVerificationStatusRequest
¶
data class SetProjectVerificationStatusRequest(
val projectId: String,
)
Properties
projectId
: String
String
ProjectsAcceptInviteLinkResponse
¶
data class ProjectsAcceptInviteLinkResponse(
val project: String,
)
Properties
project
: String
String
ProjectsRetrieveInviteLinkInfoResponse
¶
data class ProjectsRetrieveInviteLinkInfoResponse(
val token: String,
val project: Project,
val isMember: Boolean,
)