Shares

API: Internal/Beta

Shares provide users a way of collaborating on individual folders in a personal workspaces.

Rationale

📝 NOTE: This API follows the standard Resources API. We recommend that you have already read and understood the concepts described here.


This feature is currently implemented for backwards compatibility with UCloud. We don’t currently recommend other providers implement this functionality. Nevertheless, we provide a few example to give you an idea of how to use this feature. We generally recommend that you use a full-blown project for collaboration.

Table of Contents

1. Examples
Description
Complete example
2. Remote Procedure Calls
Name Description
browse Browses the catalog of available resources
browseOutgoing No description
retrieve Retrieve a single resource
retrieveProducts Retrieve product support for all accessible providers
search Searches the catalog of available resources
approve No description
create Creates one or more resources
delete Deletes one or more resources
init Request (potential) initialization of resources
reject No description
updateAcl Updates the ACL attached to a resource
updatePermissions No description
3. Data Models
Name Description
OutgoingShareGroup No description
OutgoingShareGroup.Preview No description
Share A `Resource` is the core data model used to synchronize tasks between UCloud and Provider.
Share.Spec No description
Share.State No description
Share.Status Describes the current state of the `Resource`
Share.Update Describes an update to the `Resource`
ShareFlags No description
ShareSupport No description
ShareType No description
SharesBrowseOutgoingRequest The base type for requesting paginated content.
SharesUpdatePermissionsRequestItem No description

Example: Complete example

Frequency of useCommon
Actors
  • A UCloud user named Alice (alice)
  • A UCloud user named Bob (bob)
Communication Flow: Kotlin
/* In this example we will see Alice sharing a folder with Bob. Alice starts by creating a share. The
share references a UFile. */

Shares.create.call(
    bulkRequestOf(Share.Spec(
        permissions = listOf(Permission.EDIT), 
        product = ProductReference(
            category = "example-ssd", 
            id = "example-ssd", 
            provider = "example", 
        ), 
        sharedWith = "bob", 
        sourceFilePath = "/5123/work/my-project/my-collaboration", 
    )),
    alice
).orThrow()

/*
BulkResponse(
    responses = listOf(FindByStringId(
        id = "6342", 
    )), 
)
*/

/* This returns a new ID of the Share resource. Bob can now view this when browsing the ingoing shares. */

Shares.browse.call(
    ResourceBrowseRequest(
        consistency = null, 
        flags = ShareFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterIngoing = true, 
            filterOriginalPath = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterRejected = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        itemsPerPage = null, 
        itemsToSkip = null, 
        next = null, 
        sortBy = null, 
        sortDirection = SortDirection.ascending, 
    ),
    bob
).orThrow()

/*
PageV2(
    items = listOf(Share(
        createdAt = 1635151675465, 
        id = "6342", 
        owner = ResourceOwner(
            createdBy = "alice", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.READ), 
            others = null, 
        ), 
        specification = Share.Spec(
            permissions = listOf(Permission.EDIT), 
            product = ProductReference(
                category = "example-ssd", 
                id = "example-ssd", 
                provider = "example", 
            ), 
            sharedWith = "bob", 
            sourceFilePath = "/5123/work/my-project/my-collaboration", 
        ), 
        status = Share.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            shareAvailableAt = null, 
            state = State.PENDING, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "6342", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/

/* Bob now approves this share request */

Shares.approve.call(
    bulkRequestOf(FindByStringId(
        id = "6342", 
    )),
    bob
).orThrow()

/*
Unit
*/

/* And the file is now shared and available at the path /6412 */

Shares.browse.call(
    ResourceBrowseRequest(
        consistency = null, 
        flags = ShareFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterIngoing = true, 
            filterOriginalPath = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterRejected = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        itemsPerPage = null, 
        itemsToSkip = null, 
        next = null, 
        sortBy = null, 
        sortDirection = SortDirection.ascending, 
    ),
    bob
).orThrow()

/*
PageV2(
    items = listOf(Share(
        createdAt = 1635151675465, 
        id = "6342", 
        owner = ResourceOwner(
            createdBy = "alice", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.READ), 
            others = null, 
        ), 
        specification = Share.Spec(
            permissions = listOf(Permission.EDIT), 
            product = ProductReference(
                category = "example-ssd", 
                id = "example-ssd", 
                provider = "example", 
            ), 
            sharedWith = "bob", 
            sourceFilePath = "/5123/work/my-project/my-collaboration", 
        ), 
        status = Share.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            shareAvailableAt = "/6412", 
            state = State.APPROVED, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "6342", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/
Communication Flow: TypeScript
/* In this example we will see Alice sharing a folder with Bob. Alice starts by creating a share. The
share references a UFile. */

// Authenticated as alice
await callAPI(SharesApi.create(
    {
        "items": [
            {
                "sharedWith": "bob",
                "sourceFilePath": "/5123/work/my-project/my-collaboration",
                "permissions": [
                    "EDIT"
                ],
                "product": {
                    "id": "example-ssd",
                    "category": "example-ssd",
                    "provider": "example"
                }
            }
        ]
    }
);

/*
{
    "responses": [
        {
            "id": "6342"
        }
    ]
}
*/

/* This returns a new ID of the Share resource. Bob can now view this when browsing the ingoing shares. */

// Authenticated as bob
await callAPI(SharesApi.browse(
    {
        "flags": {
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIngoing": true,
            "filterOriginalPath": null,
            "filterRejected": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "itemsPerPage": null,
        "next": null,
        "consistency": null,
        "itemsToSkip": null,
        "sortBy": null,
        "sortDirection": "ascending"
    }
);

/*
{
    "itemsPerPage": 50,
    "items": [
        {
            "id": "6342",
            "specification": {
                "sharedWith": "bob",
                "sourceFilePath": "/5123/work/my-project/my-collaboration",
                "permissions": [
                    "EDIT"
                ],
                "product": {
                    "id": "example-ssd",
                    "category": "example-ssd",
                    "provider": "example"
                }
            },
            "createdAt": 1635151675465,
            "status": {
                "shareAvailableAt": null,
                "state": "PENDING",
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "alice",
                "project": null
            },
            "permissions": {
                "myself": [
                    "READ"
                ],
                "others": null
            }
        }
    ],
    "next": null
}
*/

/* Bob now approves this share request */

await callAPI(SharesApi.approve(
    {
        "items": [
            {
                "id": "6342"
            }
        ]
    }
);

/*
{
}
*/

/* And the file is now shared and available at the path /6412 */

await callAPI(SharesApi.browse(
    {
        "flags": {
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIngoing": true,
            "filterOriginalPath": null,
            "filterRejected": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "itemsPerPage": null,
        "next": null,
        "consistency": null,
        "itemsToSkip": null,
        "sortBy": null,
        "sortDirection": "ascending"
    }
);

/*
{
    "itemsPerPage": 50,
    "items": [
        {
            "id": "6342",
            "specification": {
                "sharedWith": "bob",
                "sourceFilePath": "/5123/work/my-project/my-collaboration",
                "permissions": [
                    "EDIT"
                ],
                "product": {
                    "id": "example-ssd",
                    "category": "example-ssd",
                    "provider": "example"
                }
            },
            "createdAt": 1635151675465,
            "status": {
                "shareAvailableAt": "/6412",
                "state": "APPROVED",
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "alice",
                "project": null
            },
            "permissions": {
                "myself": [
                    "READ"
                ],
                "others": null
            }
        }
    ],
    "next": null
}
*/
Communication Flow: Curl
# ------------------------------------------------------------------------------------------------------
# $host is the UCloud instance to contact. Example: 'http://localhost:8080' or 'https://cloud.sdu.dk'
# $accessToken is a valid access-token issued by UCloud
# ------------------------------------------------------------------------------------------------------

# In this example we will see Alice sharing a folder with Bob. Alice starts by creating a share. The
# share references a UFile.

# Authenticated as alice
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/shares" -d '{
    "items": [
        {
            "sharedWith": "bob",
            "sourceFilePath": "/5123/work/my-project/my-collaboration",
            "permissions": [
                "EDIT"
            ],
            "product": {
                "id": "example-ssd",
                "category": "example-ssd",
                "provider": "example"
            }
        }
    ]
}'


# {
#     "responses": [
#         {
#             "id": "6342"
#         }
#     ]
# }

# This returns a new ID of the Share resource. Bob can now view this when browsing the ingoing shares.

# Authenticated as bob
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/shares/browse?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&filterIngoing=true&sortDirection=ascending" 

# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "6342",
#             "specification": {
#                 "sharedWith": "bob",
#                 "sourceFilePath": "/5123/work/my-project/my-collaboration",
#                 "permissions": [
#                     "EDIT"
#                 ],
#                 "product": {
#                     "id": "example-ssd",
#                     "category": "example-ssd",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635151675465,
#             "status": {
#                 "shareAvailableAt": null,
#                 "state": "PENDING",
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "alice",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "READ"
#                 ],
#                 "others": null
#             }
#         }
#     ],
#     "next": null
# }

# Bob now approves this share request

curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/shares/approve" -d '{
    "items": [
        {
            "id": "6342"
        }
    ]
}'


# {
# }

# And the file is now shared and available at the path /6412

curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/shares/browse?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&filterIngoing=true&sortDirection=ascending" 

# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "6342",
#             "specification": {
#                 "sharedWith": "bob",
#                 "sourceFilePath": "/5123/work/my-project/my-collaboration",
#                 "permissions": [
#                     "EDIT"
#                 ],
#                 "product": {
#                     "id": "example-ssd",
#                     "category": "example-ssd",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635151675465,
#             "status": {
#                 "shareAvailableAt": "/6412",
#                 "state": "APPROVED",
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "alice",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "READ"
#                 ],
#                 "others": null
#             }
#         }
#     ],
#     "next": null
# }
Communication Flow: Visual

Remote Procedure Calls

browse

API: Internal/Beta Auth: Users

Browses the catalog of available resources

Request Response Error
ResourceBrowseRequest<ShareFlags> PageV2<Share> CommonErrorMessage

browseOutgoing

API: Internal/Beta Auth: Users

Request Response Error
SharesBrowseOutgoingRequest PageV2<OutgoingShareGroup> CommonErrorMessage

retrieve

API: Internal/Beta Auth: Users

Retrieve a single resource

Request Response Error
ResourceRetrieveRequest<ShareFlags> Share CommonErrorMessage

retrieveProducts

API: Internal/Beta Auth: Users

Retrieve product support for all accessible providers

Request Response Error
Unit SupportByProvider<Product.Storage, ShareSupport> CommonErrorMessage

This endpoint will determine all providers that which the authenticated user has access to, in the current workspace. A user has access to a product, and thus a provider, if the product is either free or if the user has been granted credits to use the product.

See also:

approve

API: Internal/Beta Auth: Users

Request Response Error
BulkRequest<FindByStringId> Unit CommonErrorMessage

create

API: Internal/Beta Auth: Users

Creates one or more resources

Request Response Error
BulkRequest<Share.Spec> BulkResponse<FindByStringId> CommonErrorMessage

delete

API: Internal/Beta Auth: Users

Deletes one or more resources

Request Response Error
BulkRequest<FindByStringId> BulkResponse<Unit> CommonErrorMessage

init

API: Internal/Beta Auth: Users

Request (potential) initialization of resources

Request Response Error
Unit Unit CommonErrorMessage

This request is sent by the client, if the client believes that initialization of resources might be needed. NOTE: This request might be sent even if initialization has already taken place. UCloud/Core does not check if initialization has already taken place, it simply validates the request.

reject

API: Internal/Beta Auth: Users

Request Response Error
BulkRequest<FindByStringId> Unit CommonErrorMessage

updateAcl

API: Internal/Beta Auth: Users

Updates the ACL attached to a resource

Request Response Error
BulkRequest<UpdatedAcl> BulkResponse<Unit> CommonErrorMessage

updatePermissions

API: Internal/Beta Auth: Users

Request Response Error
BulkRequest<SharesUpdatePermissionsRequestItem> Unit CommonErrorMessage

Data Models

OutgoingShareGroup

API: Internal/Beta

data class OutgoingShareGroup(
    val sourceFilePath: String,
    val storageProduct: ProductReference,
    val sharePreview: List<OutgoingShareGroup.Preview>,
)
Properties
sourceFilePath: String
storageProduct: ProductReference
sharePreview: List<OutgoingShareGroup.Preview>

OutgoingShareGroup.Preview

API: Internal/Beta

data class Preview(
    val sharedWith: String,
    val permissions: List<Permission>,
    val state: Share.State,
    val shareId: String,
)
Properties
sharedWith: String
permissions: List<Permission>
state: Share.State
shareId: String

Share

API: Internal/Beta

A Resource is the core data model used to synchronize tasks between UCloud and Provider.

data class Share(
    val id: String,
    val specification: Share.Spec,
    val createdAt: Long,
    val status: Share.Status,
    val updates: List<Share.Update>,
    val owner: ResourceOwner,
    val permissions: ResourcePermissions?,
    val providerGeneratedId: String?,
)

For more information go here.

Properties
id: String A unique identifier referencing the `Resource`

The ID is unique across a provider for a single resource type.

specification: Share.Spec
createdAt: Long Timestamp referencing when the request for creation was received by UCloud
status: Share.Status Holds the current status of the `Resource`
updates: List<Share.Update> Contains a list of updates from the provider as well as UCloud

Updates provide a way for both UCloud, and the provider to communicate to the user what is happening with their resource.

owner: ResourceOwner Contains information about the original creator of the `Resource` along with project association
permissions: ResourcePermissions? Permissions assigned to this resource

A null value indicates that permissions are not supported by this resource type.

providerGeneratedId: String?

Share.Spec

API: Internal/Beta

data class Spec(
    val sharedWith: String,
    val sourceFilePath: String,
    val permissions: List<Permission>,
    val product: ProductReference,
)
Properties
sharedWith: String
sourceFilePath: String
permissions: List<Permission>
product: ProductReference A reference to the product which backs this `Resource`

Share.State

API: Internal/Beta

enum class State {
    APPROVED,
    REJECTED,
    PENDING,
}
Properties
APPROVED
REJECTED
PENDING

Share.Status

API: Internal/Beta

Describes the current state of the Resource

data class Status(
    val shareAvailableAt: String?,
    val state: Share.State,
    val resolvedSupport: ResolvedSupport<Product.Storage, ShareSupport>?,
    val resolvedProduct: Product.Storage?,
)

The contents of this field depends almost entirely on the specific Resource that this field is managing. Typically, this will contain information such as:

  • A state value. For example, a compute Job might be RUNNING

  • Key metrics about the resource.

  • Related resources. For example, certain Resources are bound to another Resource in a mutually exclusive way, this should be listed in the status section.

Properties
shareAvailableAt: String?
state: Share.State
resolvedSupport: ResolvedSupport<Product.Storage, ShareSupport>?
resolvedProduct: Product.Storage? The resolved product referenced by `product`.

This attribute is not included by default unless includeProduct is specified.


Share.Update

API: Internal/Beta

Describes an update to the Resource

data class Update(
    val newState: Share.State,
    val shareAvailableAt: String?,
    val timestamp: Long,
    val status: String?,
)

Updates can optionally be fetched for a Resource. The updates describe how the Resource changes state over time. The current state of a Resource can typically be read from its status field. Thus, it is typically not needed to use the full update history if you only wish to know the current state of a Resource.

An update will typically contain information similar to the status field, for example:

  • A state value. For example, a compute Job might be RUNNING.

  • Change in key metrics.

  • Bindings to related Resources.

Properties
newState: Share.State
shareAvailableAt: String?
timestamp: Long A timestamp referencing when UCloud received this update
status: String? A generic text message describing the current status of the `Resource`

ShareFlags

API: Internal/Beta

data class ShareFlags(
    val includeOthers: Boolean?,
    val includeUpdates: Boolean?,
    val includeSupport: Boolean?,
    val includeProduct: Boolean?,
    val filterCreatedBy: String?,
    val filterCreatedAfter: Long?,
    val filterCreatedBefore: Long?,
    val filterProvider: String?,
    val filterProductId: String?,
    val filterProductCategory: String?,
    val filterProviderIds: String?,
    val filterIngoing: Boolean?,
    val filterOriginalPath: String?,
    val filterRejected: String?,
    val filterIds: String?,
    val hideProductId: String?,
    val hideProductCategory: String?,
    val hideProvider: String?,
)
Properties
includeOthers: Boolean?
includeUpdates: Boolean?
includeSupport: Boolean?
includeProduct: Boolean? Includes `specification.resolvedProduct`
filterCreatedBy: String?
filterCreatedAfter: Long?
filterCreatedBefore: Long?
filterProvider: String?
filterProductId: String?
filterProductCategory: String?
filterProviderIds: String? Filters by the provider ID. The value is comma-separated.
filterIngoing: Boolean?
filterOriginalPath: String?
filterRejected: String?
filterIds: String? Filters by the resource ID. The value is comma-separated.
hideProductId: String?
hideProductCategory: String?
hideProvider: String?

ShareSupport

API: Internal/Beta

data class ShareSupport(
    val type: ShareType,
    val product: ProductReference,
)
Properties
type: ShareType
product: ProductReference

ShareType

API: Internal/Beta

enum class ShareType {
    UCLOUD_MANAGED_COLLECTION,
}
Properties
UCLOUD_MANAGED_COLLECTION

SharesBrowseOutgoingRequest

API: Internal/Beta

The base type for requesting paginated content.

data class SharesBrowseOutgoingRequest(
    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.
next: String? A token requesting the next page of items
consistency: PaginationRequestV2Consistency? Controls the consistency guarantees provided by the backend
itemsToSkip: Long? Items to skip ahead

SharesUpdatePermissionsRequestItem

API: Internal/Beta

data class SharesUpdatePermissionsRequestItem(
    val id: String,
    val permissions: List<Permission>,
)
Properties
id: String
permissions: List<Permission>