Introduction to Resources

API: Internal/Beta

Resources are the base abstraction used for orchestration in UCloud.

Rationale

In this article, we will take a closer look at what we mean when we say that UCloud is an orchestrator of resources. Before you begin, we recommend that you have already read about:

  • Providers: Exposes compute and storage resources to end-users.

  • Products: Defines the services exposed by providers.

  • Wallets: Holds allocations which grant access to products.

UCloud uses the resource abstraction to synchronize tasks between UCloud/Core and providers. As a result, resources are often used to describe work for the provider. For example, a computational Job is one type of resource used in UCloud.

To understand how resources work, we will first examine what all resources have in common:

  • A set of unique identifiers: Users and services can reference resources by using a unique ID.

  • Product and provider reference: Most resources describe a work of a provider. As a result, these resources must have a backing product.

  • A resource specification: Describes the resource. For example, this could be the parameters of a computational Job

  • Ownership and permissions: All resources have exactly one workspace owner.

  • Updates and status: Providers can send regular updates about a resource. These update describe changes in the system. These changes in turn affect the current status.

The Catalog

UCloud, in almost all cases, store a record of all resources in use. We refer to this datastore as the catalog of UCloud. As a result, UCloud/Core can fulfil some operations without involving the provider. In particular, UCloud/Core performs many read operations without the provider’s involvement.

End-users interact with all resources through a standarized API. The API provides common CRUD operations along with permission related operations. Concrete resources further extend this API with resource specific tasks. For example, virtual machines expose an operation to shut down the machine.

On this page we will discuss the end-user API. But on the following pages, you can discover the siblings of this API used by providers:

  • UCloud/Core invokes the Provider API to proxy information from the end-user API

  • The provider invokes the Control API to register changes in UCloud/Core

The Permission Model

UCloud uses a RBAC based permission model. As a workspace administrator, you must assign permissions for resources to workspace groups.

Currently, UCloud has the following permissions:

  • READ: Grants access to operations which return a resource

  • EDIT: Grants access to operations which modify a resource

  • ADMIN: Grants access to privileged operations which read or modify a resource. Workspace administrators hold this permission. It is not possible to grant this permission through example.updateAcl.

  • PROVIDER: Grants access to privileged operations which read or modify a resource. Implicit permission granted to providers of a resource. It is not possible to grant this permission through example.updateAcl.

UCloud/Core checks all permissions before proxying information to the provider. However, this doesn’t mean that an operation must succeed once it reaches a provider. Providers can perform additional permission checking once a request arrives.

Feature Detection

The resource API has support for feature detection. This happens through the example.retrieveProducts operation. Feature detection is specific to concrete resources. In general terms, feature detection can change:

  • A provider might only support a subset of fields (of a data model).

  • Some operations might be optional. A provider can declare support for advanced features.

  • A provider might only support a subset of operation workloads. They can require work to follow a certain structure. For example, a provider might declare support for containers but not for virtual machines.

A Note on the Examples

In the examples, we will work with a simple resource, used only in examples. This resource instructs the provider to count from one integer to another integer. The end-user specifies these numbers in the specification. The provider communicates the progress through updates. End-users can read the current progress from the status property.

By default, all providers support counting “forward” (in the positive direction). However, providers must declare that they support counting “backwards” (negative direction). If they do not declare support for this, then UCloud/Core will reject all requests counting backwards.

Table of Contents

1. Examples
Description
Browsing the catalog
Creating and retrieving a resource
Browsing the catalog with a filter
Searching for data
Feature detection (Supported)
Feature detection (Failure scenario)
Resource Collaboration
2. Remote Procedure Calls
Name Description
browse Browses the catalog of available resources
retrieve Retrieve a single resource
retrieveProducts Retrieve product support for all accessible providers
search Searches the catalog of available resources
create Creates one or more resources
delete Deletes one or more resources
init Request (potential) initialization of resources
updateAcl Updates the ACL attached to a resource
3. Data Models
Name Description
ResolvedSupport No description
ResourceChargeCredits No description
SortDirection No description
SupportByProvider No description
UFileUpdate Describes an update to the `Resource`
AclEntity No description
AclEntity.ProjectGroup No description
AclEntity.User No description
ExampleResource A `Resource` is the core data model used to synchronize tasks between UCloud and Provider.
ExampleResource.Spec No description
ExampleResource.State No description
ExampleResource.Status Describes the current state of the `Resource`
ExampleResource.Update Describes an update to the `Resource`
ExampleResourceFlags No description
ExampleResourceSupport No description
ExampleResourceSupport.Supported No description
Permission The UCloud permission model
ResourceAclEntry No description
ResourceUpdateAndId No description
UpdatedAclWithResource No description
ResourceBrowseRequest The base type for requesting paginated content.
ResourceInitializationRequest No description
ResourceRetrieveRequest No description
ResourceSearchRequest The base type for requesting paginated content.
ResourceChargeCreditsResponse No description

Example: Browsing the catalog

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will discover how a user can browse their catalog. This is done through the
browse operation. The browse operation exposes the results using the pagination API of UCloud.

As we will see later, it is possible to filter in the results returned using the flags of the
operation. */

Resources.browse.call(
    ResourceBrowseRequest(
        consistency = null, 
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = 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, 
    ),
    user
).orThrow()

/*
PageV2(
    items = listOf(ExampleResource(
        createdAt = 1635170395571, 
        id = "1234", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 100, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = listOf(ExampleResource.Update(
            currentValue = null, 
            newState = State.PENDING, 
            status = "We are about to start counting!", 
            timestamp = 1635170395571, 
        ), ExampleResource.Update(
            currentValue = 10, 
            newState = State.RUNNING, 
            status = "We are now counting!", 
            timestamp = 1635170395571, 
        )), 
        providerGeneratedId = "1234", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/

/* 📝 NOTE: The provider has already started counting. You can observe the changes which lead to the
current status through the updates. */
Communication Flow: TypeScript
/* In this example, we will discover how a user can browse their catalog. This is done through the
browse operation. The browse operation exposes the results using the pagination API of UCloud.

As we will see later, it is possible to filter in the results returned using the flags of the
operation. */

// Authenticated as user
await callAPI(ExampleApi.browse(
    {
        "flags": {
            "filterState": null,
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": 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": "1234",
            "specification": {
                "start": 0,
                "target": 100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
                {
                    "timestamp": 1635170395571,
                    "status": "We are about to start counting!",
                    "newState": "PENDING",
                    "currentValue": null
                },
                {
                    "timestamp": 1635170395571,
                    "status": "We are now counting!",
                    "newState": "RUNNING",
                    "currentValue": 10
                }
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        }
    ],
    "next": null
}
*/

/* 📝 NOTE: The provider has already started counting. You can observe the changes which lead to the
current status through the updates. */
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 discover how a user can browse their catalog. This is done through the
# browse operation. The browse operation exposes the results using the pagination API of UCloud.
# 
# As we will see later, it is possible to filter in the results returned using the flags of the
# operation.

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

# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "1234",
#             "specification": {
#                 "start": 0,
#                 "target": 100,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#                 {
#                     "timestamp": 1635170395571,
#                     "status": "We are about to start counting!",
#                     "newState": "PENDING",
#                     "currentValue": null
#                 },
#                 {
#                     "timestamp": 1635170395571,
#                     "status": "We are now counting!",
#                     "newState": "RUNNING",
#                     "currentValue": 10
#                 }
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         }
#     ],
#     "next": null
# }

# 📝 NOTE: The provider has already started counting. You can observe the changes which lead to the
# current status through the updates.
Communication Flow: Visual

Example: Creating and retrieving a resource

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will discover how to create a resource and retrieve information about it. */

Resources.create.call(
    bulkRequestOf(ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = 100, 
    )),
    user
).orThrow()

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

/* 📝 NOTE: Users only specify the specification when they wish to create a resource. The specification
defines the values which are in the control of the user. The specification remains immutable
for the resource's lifetime. Mutable values are instead listed in the status. */

Resources.retrieve.call(
    ResourceRetrieveRequest(
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        id = "1234", 
    ),
    user
).orThrow()

/*
ExampleResource(
    createdAt = 1635170395571, 
    id = "1234", 
    owner = ResourceOwner(
        createdBy = "user", 
        project = null, 
    ), 
    permissions = ResourcePermissions(
        myself = listOf(Permission.ADMIN), 
        others = emptyList(), 
    ), 
    specification = ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = 100, 
    ), 
    status = ExampleResource.Status(
        resolvedProduct = null, 
        resolvedSupport = null, 
        state = State.RUNNING, 
        value = 10, 
    ), 
    updates = listOf(ExampleResource.Update(
        currentValue = null, 
        newState = State.PENDING, 
        status = "We are about to start counting!", 
        timestamp = 1635170395571, 
    ), ExampleResource.Update(
        currentValue = 10, 
        newState = State.RUNNING, 
        status = "We are now counting!", 
        timestamp = 1635170395571, 
    )), 
    providerGeneratedId = "1234", 
)
*/
Communication Flow: TypeScript
/* In this example, we will discover how to create a resource and retrieve information about it. */

// Authenticated as user
await callAPI(ExampleApi.create(
    {
        "items": [
            {
                "start": 0,
                "target": 100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            }
        ]
    }
);

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

/* 📝 NOTE: Users only specify the specification when they wish to create a resource. The specification
defines the values which are in the control of the user. The specification remains immutable
for the resource's lifetime. Mutable values are instead listed in the status. */

await callAPI(ExampleApi.retrieve(
    {
        "flags": {
            "filterState": null,
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "id": "1234"
    }
);

/*
{
    "id": "1234",
    "specification": {
        "start": 0,
        "target": 100,
        "product": {
            "id": "example-compute",
            "category": "example-compute",
            "provider": "example"
        }
    },
    "createdAt": 1635170395571,
    "status": {
        "state": "RUNNING",
        "value": 10,
        "resolvedSupport": null,
        "resolvedProduct": null
    },
    "updates": [
        {
            "timestamp": 1635170395571,
            "status": "We are about to start counting!",
            "newState": "PENDING",
            "currentValue": null
        },
        {
            "timestamp": 1635170395571,
            "status": "We are now counting!",
            "newState": "RUNNING",
            "currentValue": 10
        }
    ],
    "owner": {
        "createdBy": "user",
        "project": null
    },
    "permissions": {
        "myself": [
            "ADMIN"
        ],
        "others": [
        ]
    }
}
*/
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 discover how to create a resource and retrieve information about it.

# Authenticated as user
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example" -d '{
    "items": [
        {
            "start": 0,
            "target": 100,
            "product": {
                "id": "example-compute",
                "category": "example-compute",
                "provider": "example"
            }
        }
    ]
}'


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

# 📝 NOTE: Users only specify the specification when they wish to create a resource. The specification
# defines the values which are in the control of the user. The specification remains immutable
# for the resource's lifetime. Mutable values are instead listed in the status.

curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/retrieve?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&id=1234" 

# {
#     "id": "1234",
#     "specification": {
#         "start": 0,
#         "target": 100,
#         "product": {
#             "id": "example-compute",
#             "category": "example-compute",
#             "provider": "example"
#         }
#     },
#     "createdAt": 1635170395571,
#     "status": {
#         "state": "RUNNING",
#         "value": 10,
#         "resolvedSupport": null,
#         "resolvedProduct": null
#     },
#     "updates": [
#         {
#             "timestamp": 1635170395571,
#             "status": "We are about to start counting!",
#             "newState": "PENDING",
#             "currentValue": null
#         },
#         {
#             "timestamp": 1635170395571,
#             "status": "We are now counting!",
#             "newState": "RUNNING",
#             "currentValue": 10
#         }
#     ],
#     "owner": {
#         "createdBy": "user",
#         "project": null
#     },
#     "permissions": {
#         "myself": [
#             "ADMIN"
#         ],
#         "others": [
#         ]
#     }
# }
Communication Flow: Visual

Example: Browsing the catalog with a filter

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will look at the flags which are passed to both browse and retrieve operations.
This value is used to:

- Filter out values: These properties are prefixed by filter* and remove results from the response.
  When used in a retrieve operation, this will cause a 404 if no results are found.
- Include additional data: These properties are prefixed by include* and can be used to load 
  additional data. This data is returned as part of the status object. The intention of these are to
  save the client a round-trip by retrieving all relevant data in a single call. */

Resources.browse.call(
    ResourceBrowseRequest(
        consistency = null, 
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = State.RUNNING, 
            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, 
    ),
    user
).orThrow()

/*
PageV2(
    items = listOf(ExampleResource(
        createdAt = 1635170395571, 
        id = "1234", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 100, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = listOf(ExampleResource.Update(
            currentValue = null, 
            newState = State.PENDING, 
            status = "We are about to start counting!", 
            timestamp = 1635170395571, 
        ), ExampleResource.Update(
            currentValue = 10, 
            newState = State.RUNNING, 
            status = "We are now counting!", 
            timestamp = 1635170395571, 
        )), 
        providerGeneratedId = "1234", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/
Communication Flow: TypeScript
/* In this example, we will look at the flags which are passed to both browse and retrieve operations.
This value is used to:

- Filter out values: These properties are prefixed by filter* and remove results from the response.
  When used in a retrieve operation, this will cause a 404 if no results are found.
- Include additional data: These properties are prefixed by include* and can be used to load 
  additional data. This data is returned as part of the status object. The intention of these are to
  save the client a round-trip by retrieving all relevant data in a single call. */

// Authenticated as user
await callAPI(ExampleApi.browse(
    {
        "flags": {
            "filterState": "RUNNING",
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": 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": "1234",
            "specification": {
                "start": 0,
                "target": 100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
                {
                    "timestamp": 1635170395571,
                    "status": "We are about to start counting!",
                    "newState": "PENDING",
                    "currentValue": null
                },
                {
                    "timestamp": 1635170395571,
                    "status": "We are now counting!",
                    "newState": "RUNNING",
                    "currentValue": 10
                }
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        }
    ],
    "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 look at the flags which are passed to both browse and retrieve operations.
# This value is used to:
# 
# - Filter out values: These properties are prefixed by filter* and remove results from the response.
#   When used in a retrieve operation, this will cause a 404 if no results are found.
# - Include additional data: These properties are prefixed by include* and can be used to load 
#   additional data. This data is returned as part of the status object. The intention of these are to
#   save the client a round-trip by retrieving all relevant data in a single call.

# Authenticated as user
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/browse?filterState=RUNNING&includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&sortDirection=ascending" 

# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "1234",
#             "specification": {
#                 "start": 0,
#                 "target": 100,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#                 {
#                     "timestamp": 1635170395571,
#                     "status": "We are about to start counting!",
#                     "newState": "PENDING",
#                     "currentValue": null
#                 },
#                 {
#                     "timestamp": 1635170395571,
#                     "status": "We are now counting!",
#                     "newState": "RUNNING",
#                     "currentValue": 10
#                 }
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         }
#     ],
#     "next": null
# }
Communication Flow: Visual

Example: Searching for data

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will discover the search functionality of resources. Search allows for free-text
queries which attempts to find relevant results. This is very different from browse with filters, 
since 'relevancy' is a vaguely defined concept. Search is not guaranteed to return results in any
deterministic fashion, unlike browse. */


/* We start with the following dataset. */

Resources.browse.call(
    ResourceBrowseRequest(
        consistency = null, 
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = State.RUNNING, 
            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, 
    ),
    user
).orThrow()

/*
PageV2(
    items = listOf(ExampleResource(
        createdAt = 1635170395571, 
        id = "1", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 100, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "1", 
    ), ExampleResource(
        createdAt = 1635170395571, 
        id = "2", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 200, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "2", 
    ), ExampleResource(
        createdAt = 1635170395571, 
        id = "3", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 300, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "3", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/

/* Search may look in many different fields to determine if a result is relevant. Searching for the
value 300 might produce the following results. */

Resources.search.call(
    ResourceSearchRequest(
        consistency = null, 
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        itemsPerPage = null, 
        itemsToSkip = null, 
        next = null, 
        query = "300", 
        sortBy = null, 
        sortDirection = SortDirection.ascending, 
    ),
    user
).orThrow()

/*
PageV2(
    items = listOf(ExampleResource(
        createdAt = 1635170395571, 
        id = "3", 
        owner = ResourceOwner(
            createdBy = "user", 
            project = null, 
        ), 
        permissions = ResourcePermissions(
            myself = listOf(Permission.ADMIN), 
            others = emptyList(), 
        ), 
        specification = ExampleResource.Spec(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            start = 0, 
            target = 300, 
        ), 
        status = ExampleResource.Status(
            resolvedProduct = null, 
            resolvedSupport = null, 
            state = State.RUNNING, 
            value = 10, 
        ), 
        updates = emptyList(), 
        providerGeneratedId = "3", 
    )), 
    itemsPerPage = 50, 
    next = null, 
)
*/
Communication Flow: TypeScript
/* In this example, we will discover the search functionality of resources. Search allows for free-text
queries which attempts to find relevant results. This is very different from browse with filters, 
since 'relevancy' is a vaguely defined concept. Search is not guaranteed to return results in any
deterministic fashion, unlike browse. */


/* We start with the following dataset. */

// Authenticated as user
await callAPI(ExampleApi.browse(
    {
        "flags": {
            "filterState": "RUNNING",
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": 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": "1",
            "specification": {
                "start": 0,
                "target": 100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        },
        {
            "id": "2",
            "specification": {
                "start": 0,
                "target": 200,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        },
        {
            "id": "3",
            "specification": {
                "start": 0,
                "target": 300,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        }
    ],
    "next": null
}
*/

/* Search may look in many different fields to determine if a result is relevant. Searching for the
value 300 might produce the following results. */

await callAPI(ExampleApi.search(
    {
        "flags": {
            "filterState": null,
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "query": "300",
        "itemsPerPage": null,
        "next": null,
        "consistency": null,
        "itemsToSkip": null,
        "sortBy": null,
        "sortDirection": "ascending"
    }
);

/*
{
    "itemsPerPage": 50,
    "items": [
        {
            "id": "3",
            "specification": {
                "start": 0,
                "target": 300,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            },
            "createdAt": 1635170395571,
            "status": {
                "state": "RUNNING",
                "value": 10,
                "resolvedSupport": null,
                "resolvedProduct": null
            },
            "updates": [
            ],
            "owner": {
                "createdBy": "user",
                "project": null
            },
            "permissions": {
                "myself": [
                    "ADMIN"
                ],
                "others": [
                ]
            }
        }
    ],
    "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 discover the search functionality of resources. Search allows for free-text
# queries which attempts to find relevant results. This is very different from browse with filters, 
# since 'relevancy' is a vaguely defined concept. Search is not guaranteed to return results in any
# deterministic fashion, unlike browse.

# We start with the following dataset.

# Authenticated as user
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/browse?filterState=RUNNING&includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&sortDirection=ascending" 

# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "1",
#             "specification": {
#                 "start": 0,
#                 "target": 100,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         },
#         {
#             "id": "2",
#             "specification": {
#                 "start": 0,
#                 "target": 200,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         },
#         {
#             "id": "3",
#             "specification": {
#                 "start": 0,
#                 "target": 300,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         }
#     ],
#     "next": null
# }

# Search may look in many different fields to determine if a result is relevant. Searching for the
# value 300 might produce the following results.

curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example/search" -d '{
    "flags": {
        "filterState": null,
        "includeOthers": false,
        "includeUpdates": false,
        "includeSupport": false,
        "includeProduct": false,
        "filterCreatedBy": null,
        "filterCreatedAfter": null,
        "filterCreatedBefore": null,
        "filterProvider": null,
        "filterProductId": null,
        "filterProductCategory": null,
        "filterProviderIds": null,
        "filterIds": null,
        "hideProductId": null,
        "hideProductCategory": null,
        "hideProvider": null
    },
    "query": "300",
    "itemsPerPage": null,
    "next": null,
    "consistency": null,
    "itemsToSkip": null,
    "sortBy": null,
    "sortDirection": "ascending"
}'


# {
#     "itemsPerPage": 50,
#     "items": [
#         {
#             "id": "3",
#             "specification": {
#                 "start": 0,
#                 "target": 300,
#                 "product": {
#                     "id": "example-compute",
#                     "category": "example-compute",
#                     "provider": "example"
#                 }
#             },
#             "createdAt": 1635170395571,
#             "status": {
#                 "state": "RUNNING",
#                 "value": 10,
#                 "resolvedSupport": null,
#                 "resolvedProduct": null
#             },
#             "updates": [
#             ],
#             "owner": {
#                 "createdBy": "user",
#                 "project": null
#             },
#             "permissions": {
#                 "myself": [
#                     "ADMIN"
#                 ],
#                 "others": [
#                 ]
#             }
#         }
#     ],
#     "next": null
# }
Communication Flow: Visual

Example: Feature detection (Supported)

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will show how to use the feature detection feature of resources. Recall, that
providers need to specify if they support counting backwards. */

Resources.retrieveProducts.call(
    Unit,
    user
).orThrow()

/*
SupportByProvider(
    productsByProvider = mapOf("example" to listOf(ResolvedSupport(
        product = Product.Compute(
            category = ProductCategoryId(
                id = "example-compute", 
                name = "example-compute", 
                provider = "example", 
            ), 
            chargeType = ChargeType.ABSOLUTE, 
            cpu = 1, 
            description = "An example machine", 
            freeToUse = false, 
            gpu = null, 
            hiddenInGrantApplications = false, 
            memoryInGigs = 1, 
            name = "example-compute", 
            pricePerUnit = 1, 
            priority = 0, 
            productType = ProductType.COMPUTE, 
            unitOfPrice = ProductPriceUnit.UNITS_PER_HOUR, 
            version = 1, 
            balance = null, 
            id = "example-compute", 
        ), 
        support = ExampleResourceSupport(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            supportsBackwardsCounting = Supported.SUPPORTED, 
        ), 
    ))), 
)
*/

/* In this case, the provider supports counting backwards. */


/* Creating a resource which counts backwards should succeed. */

Resources.create.call(
    bulkRequestOf(ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = -100, 
    )),
    user
).orThrow()

/*
BulkResponse(
    responses = listOf(FindByStringId(
        id = "1234", 
    )), 
)
*/
Communication Flow: TypeScript
/* In this example, we will show how to use the feature detection feature of resources. Recall, that
providers need to specify if they support counting backwards. */

// Authenticated as user
await callAPI(ExampleApi.retrieveProducts(
    {
    }
);

/*
{
    "productsByProvider": {
        "example": [
            {
                "product": {
                    "type": "compute",
                    "balance": null,
                    "name": "example-compute",
                    "pricePerUnit": 1,
                    "category": {
                        "name": "example-compute",
                        "provider": "example"
                    },
                    "description": "An example machine",
                    "priority": 0,
                    "cpu": 1,
                    "memoryInGigs": 1,
                    "gpu": null,
                    "version": 1,
                    "freeToUse": false,
                    "unitOfPrice": "UNITS_PER_HOUR",
                    "chargeType": "ABSOLUTE",
                    "hiddenInGrantApplications": false,
                    "productType": "COMPUTE"
                },
                "support": {
                    "product": {
                        "id": "example-compute",
                        "category": "example-compute",
                        "provider": "example"
                    },
                    "supportsBackwardsCounting": "SUPPORTED"
                }
            }
        ]
    }
}
*/

/* In this case, the provider supports counting backwards. */


/* Creating a resource which counts backwards should succeed. */

await callAPI(ExampleApi.create(
    {
        "items": [
            {
                "start": 0,
                "target": -100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            }
        ]
    }
);

/*
{
    "responses": [
        {
            "id": "1234"
        }
    ]
}
*/
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 show how to use the feature detection feature of resources. Recall, that
# providers need to specify if they support counting backwards.

# Authenticated as user
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/retrieveProducts" 

# {
#     "productsByProvider": {
#         "example": [
#             {
#                 "product": {
#                     "type": "compute",
#                     "balance": null,
#                     "name": "example-compute",
#                     "pricePerUnit": 1,
#                     "category": {
#                         "name": "example-compute",
#                         "provider": "example"
#                     },
#                     "description": "An example machine",
#                     "priority": 0,
#                     "cpu": 1,
#                     "memoryInGigs": 1,
#                     "gpu": null,
#                     "version": 1,
#                     "freeToUse": false,
#                     "unitOfPrice": "UNITS_PER_HOUR",
#                     "chargeType": "ABSOLUTE",
#                     "hiddenInGrantApplications": false,
#                     "productType": "COMPUTE"
#                 },
#                 "support": {
#                     "product": {
#                         "id": "example-compute",
#                         "category": "example-compute",
#                         "provider": "example"
#                     },
#                     "supportsBackwardsCounting": "SUPPORTED"
#                 }
#             }
#         ]
#     }
# }

# In this case, the provider supports counting backwards.

# Creating a resource which counts backwards should succeed.

curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example" -d '{
    "items": [
        {
            "start": 0,
            "target": -100,
            "product": {
                "id": "example-compute",
                "category": "example-compute",
                "provider": "example"
            }
        }
    ]
}'


# {
#     "responses": [
#         {
#             "id": "1234"
#         }
#     ]
# }
Communication Flow: Visual

Example: Feature detection (Failure scenario)

Frequency of useCommon
Actors
  • An authenticated user (user)
Communication Flow: Kotlin
/* In this example, we will show how to use the feature detection feature of resources. Recall, that
providers need to specify if they support counting backwards. */

Resources.retrieveProducts.call(
    Unit,
    user
).orThrow()

/*
SupportByProvider(
    productsByProvider = mapOf("example" to listOf(ResolvedSupport(
        product = Product.Compute(
            category = ProductCategoryId(
                id = "example-compute", 
                name = "example-compute", 
                provider = "example", 
            ), 
            chargeType = ChargeType.ABSOLUTE, 
            cpu = 1, 
            description = "An example machine", 
            freeToUse = false, 
            gpu = null, 
            hiddenInGrantApplications = false, 
            memoryInGigs = 1, 
            name = "example-compute", 
            pricePerUnit = 1, 
            priority = 0, 
            productType = ProductType.COMPUTE, 
            unitOfPrice = ProductPriceUnit.UNITS_PER_HOUR, 
            version = 1, 
            balance = null, 
            id = "example-compute", 
        ), 
        support = ExampleResourceSupport(
            product = ProductReference(
                category = "example-compute", 
                id = "example-compute", 
                provider = "example", 
            ), 
            supportsBackwardsCounting = Supported.NOT_SUPPORTED, 
        ), 
    ))), 
)
*/

/* In this case, the provider does not support counting backwards. */


/* Creating a resource which counts backwards should fail. */

Resources.create.call(
    bulkRequestOf(ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = -100, 
    )),
    user
).orThrow()

/*
HttpStatusCode(value=400, description=Bad Request)
*/
Communication Flow: TypeScript
/* In this example, we will show how to use the feature detection feature of resources. Recall, that
providers need to specify if they support counting backwards. */

// Authenticated as user
await callAPI(ExampleApi.retrieveProducts(
    {
    }
);

/*
{
    "productsByProvider": {
        "example": [
            {
                "product": {
                    "type": "compute",
                    "balance": null,
                    "name": "example-compute",
                    "pricePerUnit": 1,
                    "category": {
                        "name": "example-compute",
                        "provider": "example"
                    },
                    "description": "An example machine",
                    "priority": 0,
                    "cpu": 1,
                    "memoryInGigs": 1,
                    "gpu": null,
                    "version": 1,
                    "freeToUse": false,
                    "unitOfPrice": "UNITS_PER_HOUR",
                    "chargeType": "ABSOLUTE",
                    "hiddenInGrantApplications": false,
                    "productType": "COMPUTE"
                },
                "support": {
                    "product": {
                        "id": "example-compute",
                        "category": "example-compute",
                        "provider": "example"
                    },
                    "supportsBackwardsCounting": "NOT_SUPPORTED"
                }
            }
        ]
    }
}
*/

/* In this case, the provider does not support counting backwards. */


/* Creating a resource which counts backwards should fail. */

await callAPI(ExampleApi.create(
    {
        "items": [
            {
                "start": 0,
                "target": -100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            }
        ]
    }
);

/*
HttpStatusCode(value=400, description=Bad Request)
*/
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 show how to use the feature detection feature of resources. Recall, that
# providers need to specify if they support counting backwards.

# Authenticated as user
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/retrieveProducts" 

# {
#     "productsByProvider": {
#         "example": [
#             {
#                 "product": {
#                     "type": "compute",
#                     "balance": null,
#                     "name": "example-compute",
#                     "pricePerUnit": 1,
#                     "category": {
#                         "name": "example-compute",
#                         "provider": "example"
#                     },
#                     "description": "An example machine",
#                     "priority": 0,
#                     "cpu": 1,
#                     "memoryInGigs": 1,
#                     "gpu": null,
#                     "version": 1,
#                     "freeToUse": false,
#                     "unitOfPrice": "UNITS_PER_HOUR",
#                     "chargeType": "ABSOLUTE",
#                     "hiddenInGrantApplications": false,
#                     "productType": "COMPUTE"
#                 },
#                 "support": {
#                     "product": {
#                         "id": "example-compute",
#                         "category": "example-compute",
#                         "provider": "example"
#                     },
#                     "supportsBackwardsCounting": "NOT_SUPPORTED"
#                 }
#             }
#         ]
#     }
# }

# In this case, the provider does not support counting backwards.

# Creating a resource which counts backwards should fail.

curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example" -d '{
    "items": [
        {
            "start": 0,
            "target": -100,
            "product": {
                "id": "example-compute",
                "category": "example-compute",
                "provider": "example"
            }
        }
    ]
}'


# HttpStatusCode(value=400, description=Bad Request)
Communication Flow: Visual

Example: Resource Collaboration

Frequency of useCommon
Actors
  • A UCloud user named Alice (alice)
  • A UCloud user named Bob (bob)
Communication Flow: Kotlin
/* In this example, we discover how to use the resource collaboration features of UCloud. This example
involves two users: Alice and Bob. */


/* Alice starts by creating a resource */

Resources.create.call(
    bulkRequestOf(ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = 100, 
    )),
    alice
).orThrow()

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

/* By default, Bob doesn't have access to this resource. Attempting to retrieve it will fail. */

Resources.retrieve.call(
    ResourceRetrieveRequest(
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        id = "1234", 
    ),
    bob
).orThrow()

/*
HttpStatusCode(value=404, description=Not Found)
*/

/* Alice can change the permissions of the resource by invoking updateAcl. This causes Bob to gain READ permissions. */

Resources.updateAcl.call(
    bulkRequestOf(UpdatedAcl(
        added = listOf(ResourceAclEntry(
            entity = AclEntity.ProjectGroup(
                group = "Group of Bob", 
                projectId = "Project", 
            ), 
            permissions = listOf(Permission.READ), 
        )), 
        deleted = emptyList(), 
        id = "1234", 
    )),
    alice
).orThrow()

/*
BulkResponse(
    responses = listOf(Unit), 
)
*/

/* Bob can now retrieve the resource. */

Resources.retrieve.call(
    ResourceRetrieveRequest(
        flags = ExampleResourceFlags(
            filterCreatedAfter = null, 
            filterCreatedBefore = null, 
            filterCreatedBy = null, 
            filterIds = null, 
            filterProductCategory = null, 
            filterProductId = null, 
            filterProvider = null, 
            filterProviderIds = null, 
            filterState = null, 
            hideProductCategory = null, 
            hideProductId = null, 
            hideProvider = null, 
            includeOthers = false, 
            includeProduct = false, 
            includeSupport = false, 
            includeUpdates = false, 
        ), 
        id = "1234", 
    ),
    bob
).orThrow()

/*
ExampleResource(
    createdAt = 1635170395571, 
    id = "1234", 
    owner = ResourceOwner(
        createdBy = "user", 
        project = null, 
    ), 
    permissions = ResourcePermissions(
        myself = listOf(Permission.READ), 
        others = emptyList(), 
    ), 
    specification = ExampleResource.Spec(
        product = ProductReference(
            category = "example-compute", 
            id = "example-compute", 
            provider = "example", 
        ), 
        start = 0, 
        target = 100, 
    ), 
    status = ExampleResource.Status(
        resolvedProduct = null, 
        resolvedSupport = null, 
        state = State.RUNNING, 
        value = 10, 
    ), 
    updates = listOf(ExampleResource.Update(
        currentValue = null, 
        newState = State.PENDING, 
        status = "We are about to start counting!", 
        timestamp = 1635170395571, 
    ), ExampleResource.Update(
        currentValue = 10, 
        newState = State.RUNNING, 
        status = "We are now counting!", 
        timestamp = 1635170395571, 
    )), 
    providerGeneratedId = "1234", 
)
*/
Communication Flow: TypeScript
/* In this example, we discover how to use the resource collaboration features of UCloud. This example
involves two users: Alice and Bob. */


/* Alice starts by creating a resource */

// Authenticated as alice
await callAPI(ExampleApi.create(
    {
        "items": [
            {
                "start": 0,
                "target": 100,
                "product": {
                    "id": "example-compute",
                    "category": "example-compute",
                    "provider": "example"
                }
            }
        ]
    }
);

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

/* By default, Bob doesn't have access to this resource. Attempting to retrieve it will fail. */

// Authenticated as bob
await callAPI(ExampleApi.retrieve(
    {
        "flags": {
            "filterState": null,
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "id": "1234"
    }
);

/*
HttpStatusCode(value=404, description=Not Found)
*/

/* Alice can change the permissions of the resource by invoking updateAcl. This causes Bob to gain READ permissions. */

// Authenticated as alice
await callAPI(ExampleApi.updateAcl(
    {
        "items": [
            {
                "id": "1234",
                "added": [
                    {
                        "entity": {
                            "type": "project_group",
                            "projectId": "Project",
                            "group": "Group of Bob"
                        },
                        "permissions": [
                            "READ"
                        ]
                    }
                ],
                "deleted": [
                ]
            }
        ]
    }
);

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

/* Bob can now retrieve the resource. */

// Authenticated as bob
await callAPI(ExampleApi.retrieve(
    {
        "flags": {
            "filterState": null,
            "includeOthers": false,
            "includeUpdates": false,
            "includeSupport": false,
            "includeProduct": false,
            "filterCreatedBy": null,
            "filterCreatedAfter": null,
            "filterCreatedBefore": null,
            "filterProvider": null,
            "filterProductId": null,
            "filterProductCategory": null,
            "filterProviderIds": null,
            "filterIds": null,
            "hideProductId": null,
            "hideProductCategory": null,
            "hideProvider": null
        },
        "id": "1234"
    }
);

/*
{
    "id": "1234",
    "specification": {
        "start": 0,
        "target": 100,
        "product": {
            "id": "example-compute",
            "category": "example-compute",
            "provider": "example"
        }
    },
    "createdAt": 1635170395571,
    "status": {
        "state": "RUNNING",
        "value": 10,
        "resolvedSupport": null,
        "resolvedProduct": null
    },
    "updates": [
        {
            "timestamp": 1635170395571,
            "status": "We are about to start counting!",
            "newState": "PENDING",
            "currentValue": null
        },
        {
            "timestamp": 1635170395571,
            "status": "We are now counting!",
            "newState": "RUNNING",
            "currentValue": 10
        }
    ],
    "owner": {
        "createdBy": "user",
        "project": null
    },
    "permissions": {
        "myself": [
            "READ"
        ],
        "others": [
        ]
    }
}
*/
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 discover how to use the resource collaboration features of UCloud. This example
# involves two users: Alice and Bob.

# Alice starts by creating a resource

# Authenticated as alice
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example" -d '{
    "items": [
        {
            "start": 0,
            "target": 100,
            "product": {
                "id": "example-compute",
                "category": "example-compute",
                "provider": "example"
            }
        }
    ]
}'


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

# By default, Bob doesn't have access to this resource. Attempting to retrieve it will fail.

# Authenticated as bob
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/retrieve?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&id=1234" 

# HttpStatusCode(value=404, description=Not Found)

# Alice can change the permissions of the resource by invoking updateAcl. This causes Bob to gain READ permissions.

# Authenticated as alice
curl -XPOST -H "Authorization: Bearer $accessToken" -H "Content-Type: content-type: application/json; charset=utf-8" "$host/api/example/updateAcl" -d '{
    "items": [
        {
            "id": "1234",
            "added": [
                {
                    "entity": {
                        "type": "project_group",
                        "projectId": "Project",
                        "group": "Group of Bob"
                    },
                    "permissions": [
                        "READ"
                    ]
                }
            ],
            "deleted": [
            ]
        }
    ]
}'


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

# Bob can now retrieve the resource.

# Authenticated as bob
curl -XGET -H "Authorization: Bearer $accessToken" "$host/api/example/retrieve?includeOthers=false&includeUpdates=false&includeSupport=false&includeProduct=false&id=1234" 

# {
#     "id": "1234",
#     "specification": {
#         "start": 0,
#         "target": 100,
#         "product": {
#             "id": "example-compute",
#             "category": "example-compute",
#             "provider": "example"
#         }
#     },
#     "createdAt": 1635170395571,
#     "status": {
#         "state": "RUNNING",
#         "value": 10,
#         "resolvedSupport": null,
#         "resolvedProduct": null
#     },
#     "updates": [
#         {
#             "timestamp": 1635170395571,
#             "status": "We are about to start counting!",
#             "newState": "PENDING",
#             "currentValue": null
#         },
#         {
#             "timestamp": 1635170395571,
#             "status": "We are now counting!",
#             "newState": "RUNNING",
#             "currentValue": 10
#         }
#     ],
#     "owner": {
#         "createdBy": "user",
#         "project": null
#     },
#     "permissions": {
#         "myself": [
#             "READ"
#         ],
#         "others": [
#         ]
#     }
# }
Communication Flow: Visual

Remote Procedure Calls

browse

API: Internal/Beta Auth: Users

Browses the catalog of available resources

Request Response Error
ResourceBrowseRequest<ExampleResourceFlags> PageV2<ExampleResource> CommonErrorMessage

retrieve

API: Internal/Beta Auth: Users

Retrieve a single resource

Request Response Error
ResourceRetrieveRequest<ExampleResourceFlags> ExampleResource CommonErrorMessage

retrieveProducts

API: Internal/Beta Auth: Users

Retrieve product support for all accessible providers

Request Response Error
Unit SupportByProvider<Product, ExampleResourceSupport> 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:

create

API: Internal/Beta Auth: Users

Creates one or more resources

Request Response Error
BulkRequest<ExampleResource.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.

updateAcl

API: Internal/Beta Auth: Users

Updates the ACL attached to a resource

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

Data Models

ResolvedSupport

API: Internal/Beta

data class ResolvedSupport<P, Support>(
    val product: P,
    val support: Support,
)
Properties
product: P
support: Support

ResourceChargeCredits

API: Internal/Beta

data class ResourceChargeCredits(
    val id: String,
    val chargeId: String,
    val units: Long,
    val periods: Long?,
    val performedBy: String?,
    val description: String?,
)
Properties
id: String The ID of the `Resource`
chargeId: String The ID of the charge

This charge ID must be unique for the Resource, UCloud will reject charges which are not unique.

units: Long Amount of units to charge the user
periods: Long?
performedBy: String?
description: String?

SortDirection

API: Internal/Beta

enum class SortDirection {
    ascending,
    descending,
}
Properties
ascending
descending

SupportByProvider

API: Internal/Beta

data class SupportByProvider<P, S>(
    val productsByProvider: JsonObject,
)
Properties
productsByProvider: JsonObject

UFileUpdate

API: Internal/Beta

Describes an update to the Resource

data class UFileUpdate(
    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
timestamp: Long A timestamp referencing when UCloud received this update
status: String? A generic text message describing the current status of the `Resource`

AclEntity

API: Internal/Beta

sealed class AclEntity {
    class ProjectGroup : AclEntity()
    class User : AclEntity()
}

AclEntity.ProjectGroup

API: Internal/Beta

data class ProjectGroup(
    val projectId: String,
    val group: String,
    val type: String /* "project_group" */,
)
Properties
projectId: String
group: String
type: String /* "project_group" */ The type discriminator

API: Stable


AclEntity.User

API: Internal/Beta

data class User(
    val username: String,
    val type: String /* "user" */,
)
Properties
username: String
type: String /* "user" */ The type discriminator

API: Stable


ExampleResource

API: Internal/Beta

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

data class ExampleResource(
    val id: String,
    val specification: ExampleResource.Spec,
    val createdAt: Long,
    val status: ExampleResource.Status,
    val updates: List<ExampleResource.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: ExampleResource.Spec
createdAt: Long Timestamp referencing when the request for creation was received by UCloud
status: ExampleResource.Status Holds the current status of the `Resource`
updates: List<ExampleResource.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?

ExampleResource.Spec

API: Internal/Beta

data class Spec(
    val start: Int,
    val target: Int,
    val product: ProductReference,
)
Properties
start: Int
target: Int
product: ProductReference A reference to the product which backs this `Resource`

ExampleResource.State

API: Internal/Beta

enum class State {
    PENDING,
    RUNNING,
    DONE,
}
Properties
PENDING
RUNNING
DONE

ExampleResource.Status

API: Internal/Beta

Describes the current state of the Resource

data class Status(
    val state: ExampleResource.State,
    val value: Int,
    val resolvedSupport: ResolvedSupport<Product, ExampleResourceSupport>?,
    val resolvedProduct: Product?,
)

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
state: ExampleResource.State
value: Int
resolvedSupport: ResolvedSupport<Product, ExampleResourceSupport>?
resolvedProduct: Product? The resolved product referenced by `product`.

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


ExampleResource.Update

API: Internal/Beta

Describes an update to the Resource

data class Update(
    val timestamp: Long?,
    val status: String?,
    val newState: ExampleResource.State?,
    val currentValue: Int?,
)

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
timestamp: Long? A timestamp referencing when UCloud received this update
status: String? A generic text message describing the current status of the `Resource`
newState: ExampleResource.State?
currentValue: Int?

ExampleResourceFlags

API: Internal/Beta

data class ExampleResourceFlags(
    val filterState: ExampleResource.State?,
    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 filterIds: String?,
    val hideProductId: String?,
    val hideProductCategory: String?,
    val hideProvider: String?,
)
Properties
filterState: ExampleResource.State?
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.
filterIds: String? Filters by the resource ID. The value is comma-separated.
hideProductId: String?
hideProductCategory: String?
hideProvider: String?

ExampleResourceSupport

API: Internal/Beta

data class ExampleResourceSupport(
    val product: ProductReference,
    val supportsBackwardsCounting: ExampleResourceSupport.Supported?,
)
Properties
product: ProductReference
supportsBackwardsCounting: ExampleResourceSupport.Supported?

ExampleResourceSupport.Supported

API: Internal/Beta

enum class Supported {
    SUPPORTED,
    NOT_SUPPORTED,
}
Properties
SUPPORTED
NOT_SUPPORTED

Permission

API: Experimental/Alpha

The UCloud permission model

enum class Permission {
    READ,
    EDIT,
    ADMIN,
    PROVIDER,
}

This type covers the permission part of UCloud’s RBAC based authorization model. UCloud defines a set of standard permissions that can be applied to a resource and its associated operations.

Properties
READ Grants an entity access to all read-based operations

Read-based operations must not alter the state of a resource. Typical examples include the browse and retrieve* endpoints.

EDIT Grants an entity access to all write-based operations

Write-based operations are allowed to alter the state of a resource. This permission is required for most update* endpoints.

ADMIN Grants an entity access to special privileged operations

This permission will allow the entity to perform any action on the resource, unless the operation specifies otherwise. This operation is, for example, used for updating the permissions attached to a resource.

PROVIDER Grants an entity access to special privileged operations specific to a provider

ResourceAclEntry

API: Internal/Beta

data class ResourceAclEntry(
    val entity: AclEntity,
    val permissions: List<Permission>,
)
Properties
entity: AclEntity
permissions: List<Permission>

ResourceUpdateAndId

API: Internal/Beta

data class ResourceUpdateAndId<U>(
    val id: String,
    val update: U,
)
Properties
id: String
update: U

UpdatedAclWithResource

API: Internal/Beta

data class UpdatedAclWithResource<Res>(
    val resource: Res,
    val added: List<ResourceAclEntry>,
    val deleted: List<AclEntity>,
)
Properties
resource: Res
added: List<ResourceAclEntry>
deleted: List<AclEntity>

ResourceBrowseRequest

API: Internal/Beta

The base type for requesting paginated content.

data class ResourceBrowseRequest<Flags>(
    val flags: Flags,
    val itemsPerPage: Int?,
    val next: String?,
    val consistency: PaginationRequestV2Consistency?,
    val itemsToSkip: Long?,
    val sortBy: String?,
    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
flags: Flags
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
sortBy: String?
sortDirection: SortDirection?

ResourceInitializationRequest

API: Internal/Beta

data class ResourceInitializationRequest(
    val principal: ResourceOwner,
)
Properties
principal: ResourceOwner

ResourceRetrieveRequest

API: Internal/Beta

data class ResourceRetrieveRequest<Flags>(
    val flags: Flags,
    val id: String,
)
Properties
flags: Flags
id: String

ResourceSearchRequest

API: Internal/Beta

The base type for requesting paginated content.

data class ResourceSearchRequest<Flags>(
    val flags: Flags,
    val query: String,
    val itemsPerPage: Int?,
    val next: String?,
    val consistency: PaginationRequestV2Consistency?,
    val itemsToSkip: Long?,
    val sortBy: String?,
    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
flags: Flags
query: String
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
sortBy: String?
sortDirection: SortDirection?

ResourceChargeCreditsResponse

API: Internal/Beta

data class ResourceChargeCreditsResponse(
    val insufficientFunds: List<FindByStringId>,
    val duplicateCharges: List<FindByStringId>,
)
Properties
insufficientFunds: List<FindByStringId> A list of resources which could not be charged due to lack of funds. If all resources were charged successfully then this will empty.
duplicateCharges: List<FindByStringId> A list of resources which could not be charged due to it being a duplicate charge. If all resources were charged successfully this will be empty.