diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index fe2ec52dc..cc851bd8e 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -213,8 +213,7 @@ const sdk = fromSharedOptions();
* [.remove(slugOrUuidOrIdOrIds)](#balena.models.application.remove) ⇒ Promise
* [.rename(slugOrUuidOrId, newName)](#balena.models.application.rename) ⇒ Promise
* [.restart(slugOrUuidOrId)](#balena.models.application.restart) ⇒ Promise
- * ~~[.generateApiKey(slugOrUuidOrId)](#balena.models.application.generateApiKey) ⇒ Promise
~~
- * [.generateProvisioningKey(slugOrUuidOrId, [keyName], [keyDescription], [keyExpiryDate])](#balena.models.application.generateProvisioningKey) ⇒ Promise
+ * [.generateProvisioningKey(generateProvisioningKeyParams)](#balena.models.application.generateProvisioningKey) ⇒ Promise
* [.purge(appId)](#balena.models.application.purge) ⇒ Promise
* [.shutdown(appId, [options])](#balena.models.application.shutdown) ⇒ Promise
* [.reboot(appId, [options])](#balena.models.application.reboot) ⇒ Promise
@@ -302,13 +301,9 @@ const sdk = fromSharedOptions();
* [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise
* [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise
* [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise
- * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise
~~
* [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise
- * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise
~~
* [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise
* [.restartApplication(uuidOrId)](#balena.models.device.restartApplication) ⇒ Promise
- * ~~[.startApplication(uuidOrId)](#balena.models.device.startApplication) ⇒ Promise
~~
- * ~~[.stopApplication(uuidOrId)](#balena.models.device.stopApplication) ⇒ Promise
~~
* [.reboot(uuidOrId, [options])](#balena.models.device.reboot) ⇒ Promise
* [.shutdown(uuidOrId, [options])](#balena.models.device.shutdown) ⇒ Promise
* [.purge(uuidOrId)](#balena.models.device.purge) ⇒ Promise
@@ -328,7 +323,7 @@ const sdk = fromSharedOptions();
* [.getInstructions(deviceTypeSlugOrContract)](#balena.models.deviceType.getInstructions) ⇒ Promise
* [.getInstallMethod(deviceTypeSlug)](#balena.models.deviceType.getInstallMethod) ⇒ Promise
* [.apiKey](#balena.models.apiKey) : object
- * [.create(name, [description])](#balena.models.apiKey.create) ⇒ Promise
+ * [.create(createApiKeyParams)](#balena.models.apiKey.create) ⇒ Promise
* [.getAll([options])](#balena.models.apiKey.getAll) ⇒ Promise
* [.getAllNamedUserApiKeys([options])](#balena.models.apiKey.getAllNamedUserApiKeys) ⇒ Promise
* [.getProvisioningApiKeysByApplication(slugOrUuidOrId, [options])](#balena.models.apiKey.getProvisioningApiKeysByApplication) ⇒ Promise
@@ -345,7 +340,6 @@ const sdk = fromSharedOptions();
* [.get(membershipId, [options])](#balena.models.organization.membership.get) ⇒ Promise
* [.getAllByOrganization(handleOrId, [options])](#balena.models.organization.membership.getAllByOrganization) ⇒ Promise
* [.getAllByUser(usernameOrId, [options])](#balena.models.organization.membership.getAllByUser) ⇒ Promise
- * ~~[.create(options)](#balena.models.organization.membership.create) ⇒ Promise
~~
* [.changeRole(idOrUniqueKey, roleName)](#balena.models.organization.membership.changeRole) ⇒ Promise
* [.remove(id)](#balena.models.organization.membership.remove) ⇒ Promise
* [.invite](#balena.models.organization.invite) : object
@@ -363,7 +357,6 @@ const sdk = fromSharedOptions();
* [.getAllOsVersions(deviceTypes, [options])](#balena.models.os.getAllOsVersions) ⇒ Promise
* [.getDownloadSize(deviceType, [version])](#balena.models.os.getDownloadSize) ⇒ Promise
* [.getMaxSatisfyingVersion(deviceType, versionOrRange, [osType])](#balena.models.os.getMaxSatisfyingVersion) ⇒ Promise
- * [.getLastModified(deviceType, [version])](#balena.models.os.getLastModified) ⇒ Promise
* [.download(options)](#balena.models.os.download) ⇒ Promise
* [.getConfig(slugOrUuidOrId, options)](#balena.models.os.getConfig) ⇒ Promise
* [.isSupportedOsUpdate(deviceType, currentVersion, targetVersion)](#balena.models.os.isSupportedOsUpdate) ⇒ Promise
@@ -622,8 +615,7 @@ balena.models.device.get(123).catch(function (error) {
* [.remove(slugOrUuidOrIdOrIds)](#balena.models.application.remove) ⇒ Promise
* [.rename(slugOrUuidOrId, newName)](#balena.models.application.rename) ⇒ Promise
* [.restart(slugOrUuidOrId)](#balena.models.application.restart) ⇒ Promise
- * ~~[.generateApiKey(slugOrUuidOrId)](#balena.models.application.generateApiKey) ⇒ Promise
~~
- * [.generateProvisioningKey(slugOrUuidOrId, [keyName], [keyDescription], [keyExpiryDate])](#balena.models.application.generateProvisioningKey) ⇒ Promise
+ * [.generateProvisioningKey(generateProvisioningKeyParams)](#balena.models.application.generateProvisioningKey) ⇒ Promise
* [.purge(appId)](#balena.models.application.purge) ⇒ Promise
* [.shutdown(appId, [options])](#balena.models.application.shutdown) ⇒ Promise
* [.reboot(appId, [options])](#balena.models.application.reboot) ⇒ Promise
@@ -711,13 +703,9 @@ balena.models.device.get(123).catch(function (error) {
* [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise
* [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise
* [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise
- * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise
~~
* [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise
- * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise
~~
* [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise
* [.restartApplication(uuidOrId)](#balena.models.device.restartApplication) ⇒ Promise
- * ~~[.startApplication(uuidOrId)](#balena.models.device.startApplication) ⇒ Promise
~~
- * ~~[.stopApplication(uuidOrId)](#balena.models.device.stopApplication) ⇒ Promise
~~
* [.reboot(uuidOrId, [options])](#balena.models.device.reboot) ⇒ Promise
* [.shutdown(uuidOrId, [options])](#balena.models.device.shutdown) ⇒ Promise
* [.purge(uuidOrId)](#balena.models.device.purge) ⇒ Promise
@@ -737,7 +725,7 @@ balena.models.device.get(123).catch(function (error) {
* [.getInstructions(deviceTypeSlugOrContract)](#balena.models.deviceType.getInstructions) ⇒ Promise
* [.getInstallMethod(deviceTypeSlug)](#balena.models.deviceType.getInstallMethod) ⇒ Promise
* [.apiKey](#balena.models.apiKey) : object
- * [.create(name, [description])](#balena.models.apiKey.create) ⇒ Promise
+ * [.create(createApiKeyParams)](#balena.models.apiKey.create) ⇒ Promise
* [.getAll([options])](#balena.models.apiKey.getAll) ⇒ Promise
* [.getAllNamedUserApiKeys([options])](#balena.models.apiKey.getAllNamedUserApiKeys) ⇒ Promise
* [.getProvisioningApiKeysByApplication(slugOrUuidOrId, [options])](#balena.models.apiKey.getProvisioningApiKeysByApplication) ⇒ Promise
@@ -754,7 +742,6 @@ balena.models.device.get(123).catch(function (error) {
* [.get(membershipId, [options])](#balena.models.organization.membership.get) ⇒ Promise
* [.getAllByOrganization(handleOrId, [options])](#balena.models.organization.membership.getAllByOrganization) ⇒ Promise
* [.getAllByUser(usernameOrId, [options])](#balena.models.organization.membership.getAllByUser) ⇒ Promise
- * ~~[.create(options)](#balena.models.organization.membership.create) ⇒ Promise
~~
* [.changeRole(idOrUniqueKey, roleName)](#balena.models.organization.membership.changeRole) ⇒ Promise
* [.remove(id)](#balena.models.organization.membership.remove) ⇒ Promise
* [.invite](#balena.models.organization.invite) : object
@@ -772,7 +759,6 @@ balena.models.device.get(123).catch(function (error) {
* [.getAllOsVersions(deviceTypes, [options])](#balena.models.os.getAllOsVersions) ⇒ Promise
* [.getDownloadSize(deviceType, [version])](#balena.models.os.getDownloadSize) ⇒ Promise
* [.getMaxSatisfyingVersion(deviceType, versionOrRange, [osType])](#balena.models.os.getMaxSatisfyingVersion) ⇒ Promise
- * [.getLastModified(deviceType, [version])](#balena.models.os.getLastModified) ⇒ Promise
* [.download(options)](#balena.models.os.download) ⇒ Promise
* [.getConfig(slugOrUuidOrId, options)](#balena.models.os.getConfig) ⇒ Promise
* [.isSupportedOsUpdate(deviceType, currentVersion, targetVersion)](#balena.models.os.isSupportedOsUpdate) ⇒ Promise
@@ -879,8 +865,7 @@ balena.models.device.get(123).catch(function (error) {
* [.remove(slugOrUuidOrIdOrIds)](#balena.models.application.remove) ⇒ Promise
* [.rename(slugOrUuidOrId, newName)](#balena.models.application.rename) ⇒ Promise
* [.restart(slugOrUuidOrId)](#balena.models.application.restart) ⇒ Promise
- * ~~[.generateApiKey(slugOrUuidOrId)](#balena.models.application.generateApiKey) ⇒ Promise
~~
- * [.generateProvisioningKey(slugOrUuidOrId, [keyName], [keyDescription], [keyExpiryDate])](#balena.models.application.generateProvisioningKey) ⇒ Promise
+ * [.generateProvisioningKey(generateProvisioningKeyParams)](#balena.models.application.generateProvisioningKey) ⇒ Promise
* [.purge(appId)](#balena.models.application.purge) ⇒ Promise
* [.shutdown(appId, [options])](#balena.models.application.shutdown) ⇒ Promise
* [.reboot(appId, [options])](#balena.models.application.reboot) ⇒ Promise
@@ -1387,7 +1372,7 @@ balena.models.application.membership.getAllByUser(123).then(function(memberships
###### membership.create(options) ⇒ Promise
-This method adds a user to an application by their username.
+This method adds a user to an application by their username if they are a member of the organization.
**Kind**: static method of [membership
](#balena.models.application.membership)
**Summary**: Creates a new membership for an application
@@ -1889,39 +1874,9 @@ balena.models.application.restart('myorganization/myapp');
```js
balena.models.application.restart(123);
```
-
-
-##### ~~application.generateApiKey(slugOrUuidOrId) ⇒ Promise
~~
-***Deprecated***
-
-Generally you shouldn't use this method: if you're provisioning a recent BalenaOS
-version (2.4.0+) then generateProvisioningKey should work just as well, but
-be more secure.
-
-**Kind**: static method of [application
](#balena.models.application)
-**Summary**: Generate an API key for a specific application
-**Access**: public
-**Fulfil**: String
- api key
-
-| Param | Type | Description |
-| --- | --- | --- |
-| slugOrUuidOrId | String
\| Number
| application slug (string), uuid (string) or id (number) |
-
-**Example**
-```js
-balena.models.application.generateApiKey('myorganization/myapp').then(function(apiKey) {
- console.log(apiKey);
-});
-```
-**Example**
-```js
-balena.models.application.generateApiKey(123).then(function(apiKey) {
- console.log(apiKey);
-});
-```
-##### application.generateProvisioningKey(slugOrUuidOrId, [keyName], [keyDescription], [keyExpiryDate]) ⇒ Promise
+##### application.generateProvisioningKey(generateProvisioningKeyParams) ⇒ Promise
**Kind**: static method of [application
](#balena.models.application)
**Summary**: Generate a device provisioning key for a specific application
**Access**: public
@@ -1929,26 +1884,27 @@ balena.models.application.generateApiKey(123).then(function(apiKey) {
| Param | Type | Description |
| --- | --- | --- |
-| slugOrUuidOrId | String
\| Number
| application slug (string), uuid (string) or id (number) |
-| [keyName] | String
| Provisioning key name |
-| [keyDescription] | String
| Description for provisioning key |
-| [keyExpiryDate] | String
| Expiry Date for provisioning key |
+| generateProvisioningKeyParams | Object
| an object containing the parameters for the provisioning key generation |
+| generateProvisioningKeyParams.slugOrUuidOrId | String
\| Number
| application slug (string), uuid (string) or id (number) |
+| generateProvisioningKeyParams.keyExpiryDate | String
| Expiry Date for provisioning key |
+| [generateProvisioningKeyParams.keyName] | String
| Provisioning key name |
+| [generateProvisioningKeyParams.keyDescription] | String
| Description for provisioning key |
**Example**
```js
-balena.models.application.generateProvisioningKey('myorganization/myapp').then(function(key) {
+balena.models.application.generateProvisioningKey({slugOrUuidOrId: 'myorganization/myapp', keyExpiryDate: '2030-10-12'}).then(function(key) {
console.log(key);
});
```
**Example**
```js
-balena.models.application.generateProvisioningKey(123).then(function(key) {
+balena.models.application.generateProvisioningKey({slugOrUuidOrId: 123, keyExpiryDate: '2030-10-12'}).then(function(key) {
console.log(key);
});
```
**Example**
```js
-balena.models.application.generateProvisioningKey(123, 'api key name', 'api key long description', '2030-01-01T00:00:00Z').then(function(key) {
+balena.models.application.generateProvisioningKey({slugOrUuidOrId: 123, keyExpiryDate: '2030-10-12', keyName: 'api key name', keyDescription: 'api key long description'}).then(function(key) {
console.log(key);
});
```
@@ -2288,13 +2244,9 @@ balena.models.application.revokeSupportAccess(123);
* [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise
* [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise
* [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise
- * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise
~~
* [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise
- * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise
~~
* [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise
* [.restartApplication(uuidOrId)](#balena.models.device.restartApplication) ⇒ Promise
- * ~~[.startApplication(uuidOrId)](#balena.models.device.startApplication) ⇒ Promise
~~
- * ~~[.stopApplication(uuidOrId)](#balena.models.device.stopApplication) ⇒ Promise
~~
* [.reboot(uuidOrId, [options])](#balena.models.device.reboot) ⇒ Promise
* [.shutdown(uuidOrId, [options])](#balena.models.device.shutdown) ⇒ Promise
* [.purge(uuidOrId)](#balena.models.device.purge) ⇒ Promise
@@ -4032,7 +3984,7 @@ balena.models.device.setSupervisorRelease(123, '11.4.14').then(function() {
| uuidOrUuids | String
\| Array.<String>
| full device uuid or array of full uuids |
| targetOsVersion | String
| semver-compatible version for the target device Unsupported (unpublished) version will result in rejection. The version **must** be the exact version number, a "prod" variant and greater than the one running on the device. To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`. |
| [options] | Object
| options |
-| [options.runDetached] | Boolean
| run the update in detached mode. Default behaviour is runDetached=false but is DEPRECATED and will be removed in a future release. Use runDetached=true for more reliable updates. |
+| [options.runDetached] | Boolean
| run the update in detached mode. True by default |
**Example**
```js
@@ -4040,26 +3992,6 @@ balena.models.device.startOsUpdate('7cf02a687b74206f92cb455969cf8e98', '2.29.2+r
console.log(result.status);
});
```
-
-
-##### ~~device.getOsUpdateStatus(uuid) ⇒ Promise
~~
-***Deprecated***
-
-**Kind**: static method of [device
](#balena.models.device)
-**Summary**: Get the OS update status of a device. This will no longer return a useful status for runDetached=true updates.
-**Access**: public
-**Fulfil**: Object
- action response
-
-| Param | Type | Description |
-| --- | --- | --- |
-| uuid | String
| full device uuid |
-
-**Example**
-```js
-balena.models.device.getOsUpdateStatus('7cf02a687b74206f92cb455969cf8e98').then(function(status) {
- console.log(result.status);
-});
-```
##### device.ping(uuidOrId) ⇒ Promise
@@ -4081,34 +4013,6 @@ balena.models.device.ping('7cf02a6');
```js
balena.models.device.ping(123);
```
-
-
-##### ~~device.getApplicationInfo(uuidOrId) ⇒ Promise
~~
-***Deprecated***
-
-This is not supported on multicontainer devices, and will be removed in a future major release
-
-**Kind**: static method of [device
](#balena.models.device)
-**Summary**: Get application container information
-**Access**: public
-**Fulfil**: Object
- application info
-
-| Param | Type | Description |
-| --- | --- | --- |
-| uuidOrId | String
\| Number
| device uuid (string) or id (number) |
-
-**Example**
-```js
-balena.models.device.getApplicationInfo('7cf02a6').then(function(appInfo) {
- console.log(appInfo);
-});
-```
-**Example**
-```js
-balena.models.device.getApplicationInfo(123).then(function(appInfo) {
- console.log(appInfo);
-});
-```
##### device.identify(uuidOrId) ⇒ Promise
@@ -4151,62 +4055,6 @@ balena.models.device.restartApplication('7cf02a6');
```js
balena.models.device.restartApplication(123);
```
-
-
-##### ~~device.startApplication(uuidOrId) ⇒ Promise
~~
-***Deprecated***
-
-This is not supported on multicontainer devices, and will be removed in a future major release
-
-**Kind**: static method of [device
](#balena.models.device)
-**Summary**: Start application on device
-**Access**: public
-**Fulfil**: String
- application container id
-
-| Param | Type | Description |
-| --- | --- | --- |
-| uuidOrId | String
\| Number
| device uuid (string) or id (number) |
-
-**Example**
-```js
-balena.models.device.startApplication('7cf02a6').then(function(containerId) {
- console.log(containerId);
-});
-```
-**Example**
-```js
-balena.models.device.startApplication(123).then(function(containerId) {
- console.log(containerId);
-});
-```
-
-
-##### ~~device.stopApplication(uuidOrId) ⇒ Promise
~~
-***Deprecated***
-
-This is not supported on multicontainer devices, and will be removed in a future major release
-
-**Kind**: static method of [device
](#balena.models.device)
-**Summary**: Stop application on device
-**Access**: public
-**Fulfil**: String
- application container id
-
-| Param | Type | Description |
-| --- | --- | --- |
-| uuidOrId | String
\| Number
| device uuid (string) or id (number) |
-
-**Example**
-```js
-balena.models.device.stopApplication('7cf02a6').then(function(containerId) {
- console.log(containerId);
-});
-```
-**Example**
-```js
-balena.models.device.stopApplication(123).then(function(containerId) {
- console.log(containerId);
-});
-```
##### device.reboot(uuidOrId, [options]) ⇒ Promise
@@ -4613,7 +4461,7 @@ balena.models.deviceType.getInstallMethod('raspberry-pi').then(function(method)
**Kind**: static namespace of [models
](#balena.models)
* [.apiKey](#balena.models.apiKey) : object
- * [.create(name, [description])](#balena.models.apiKey.create) ⇒ Promise
+ * [.create(createApiKeyParams)](#balena.models.apiKey.create) ⇒ Promise
* [.getAll([options])](#balena.models.apiKey.getAll) ⇒ Promise
* [.getAllNamedUserApiKeys([options])](#balena.models.apiKey.getAllNamedUserApiKeys) ⇒ Promise
* [.getProvisioningApiKeysByApplication(slugOrUuidOrId, [options])](#balena.models.apiKey.getProvisioningApiKeysByApplication) ⇒ Promise
@@ -4623,7 +4471,7 @@ balena.models.deviceType.getInstallMethod('raspberry-pi').then(function(method)
-##### apiKey.create(name, [description]) ⇒ Promise
+##### apiKey.create(createApiKeyParams) ⇒ Promise
This method registers a new api key for the current user with the name given.
**Kind**: static method of [apiKey
](#balena.models.apiKey)
@@ -4633,18 +4481,20 @@ This method registers a new api key for the current user with the name given.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
-| name | String
| | the API key name |
-| [description] | String
|
| the API key description |
+| createApiKeyParams | Object
| | an object containing the parameters for the creation of an API key |
+| createApiKeyParams.name | String
| | the API key name |
+| createApiKeyParams.expiryDate | String
| | the API key expiry date |
+| [createApiKeyParams.description] | String
|
| the API key description |
**Example**
```js
-balena.models.apiKey.create(apiKeyName).then(function(apiKey) {
+balena.models.apiKey.create({name: apiKeyName, expiryDate: 2030-10-12}).then(function(apiKey) {
console.log(apiKey);
});
```
**Example**
```js
-balena.models.apiKey.create(apiKeyName, apiKeyDescription).then(function(apiKey) {
+balena.models.apiKey.create({name: apiKeyName, expiryDate: 2030-10-12, description: apiKeyDescription}).then(function(apiKey) {
console.log(apiKey);
});
```
@@ -4856,7 +4706,6 @@ balena.models.key.create('Main', 'ssh-rsa AAAAB....').then(function(key) {
* [.get(membershipId, [options])](#balena.models.organization.membership.get) ⇒ Promise
* [.getAllByOrganization(handleOrId, [options])](#balena.models.organization.membership.getAllByOrganization) ⇒ Promise
* [.getAllByUser(usernameOrId, [options])](#balena.models.organization.membership.getAllByUser) ⇒ Promise
- * ~~[.create(options)](#balena.models.organization.membership.create) ⇒ Promise
~~
* [.changeRole(idOrUniqueKey, roleName)](#balena.models.organization.membership.changeRole) ⇒ Promise
* [.remove(id)](#balena.models.organization.membership.remove) ⇒ Promise
* [.invite](#balena.models.organization.invite) : object
@@ -4879,7 +4728,6 @@ balena.models.key.create('Main', 'ssh-rsa AAAAB....').then(function(key) {
* [.get(membershipId, [options])](#balena.models.organization.membership.get) ⇒ Promise
* [.getAllByOrganization(handleOrId, [options])](#balena.models.organization.membership.getAllByOrganization) ⇒ Promise
* [.getAllByUser(usernameOrId, [options])](#balena.models.organization.membership.getAllByUser) ⇒ Promise
- * ~~[.create(options)](#balena.models.organization.membership.create) ⇒ Promise
~~
* [.changeRole(idOrUniqueKey, roleName)](#balena.models.organization.membership.changeRole) ⇒ Promise
* [.remove(id)](#balena.models.organization.membership.remove) ⇒ Promise
@@ -4958,32 +4806,6 @@ balena.models.organization.membership.getAllByUser(123).then(function(membership
console.log(memberships);
});
```
-
-
-###### ~~membership.create(options) ⇒ Promise
~~
-***use balena.models.organization.invite.create instead***
-
-This method adds a user to an organization by their usename.
-WARNING: This method is deprecated, use balena.models.organization.invite.create instead.
-
-**Kind**: static method of [membership
](#balena.models.organization.membership)
-**Summary**: Creates a new membership for an organization
-**Access**: public
-**Fulfil**: Object
- organization membership
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| options | Object
| | membership creation parameters |
-| options.organization | String
\| Number
| | organization handle (string), or id (number) |
-| options.username | String
| | the username of the balena user that will become a member |
-| [options.roleName] | String
| "member"
| the role name to be granted to the membership |
-
-**Example**
-```js
-balena.models.organization.membership.create({ organization: "myorg", username: "user123", roleName: "member" }).then(function(membership) {
- console.log(membership);
-});
-```
###### membership.changeRole(idOrUniqueKey, roleName) ⇒ Promise
@@ -5267,7 +5089,6 @@ balena.models.organization.remove(123);
* [.getAllOsVersions(deviceTypes, [options])](#balena.models.os.getAllOsVersions) ⇒ Promise
* [.getDownloadSize(deviceType, [version])](#balena.models.os.getDownloadSize) ⇒ Promise
* [.getMaxSatisfyingVersion(deviceType, versionOrRange, [osType])](#balena.models.os.getMaxSatisfyingVersion) ⇒ Promise
- * [.getLastModified(deviceType, [version])](#balena.models.os.getLastModified) ⇒ Promise
* [.download(options)](#balena.models.os.download) ⇒ Promise
* [.getConfig(slugOrUuidOrId, options)](#balena.models.os.getConfig) ⇒ Promise
* [.isSupportedOsUpdate(deviceType, currentVersion, targetVersion)](#balena.models.os.isSupportedOsUpdate) ⇒ Promise
@@ -5366,29 +5187,6 @@ balena.models.os.getMaxSatisfyingVersion('raspberry-pi', '^2.11.0').then(functio
console.log(version);
});
```
-
-
-##### os.getLastModified(deviceType, [version]) ⇒ Promise
-**Kind**: static method of [os
](#balena.models.os)
-**Summary**: Get the OS image last modified date
-**Access**: public
-**Fulfil**: Date
- last modified date
-
-| Param | Type | Description |
-| --- | --- | --- |
-| deviceType | String
| device type slug |
-| [version] | String
| semver-compatible version or 'latest', defaults to 'latest'. Unsupported (unpublished) version will result in rejection. The version **must** be the exact version number. To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`. |
-
-**Example**
-```js
-balena.models.os.getLastModified('raspberry-pi').then(function(date) {
- console.log('The raspberry-pi image was last modified in ' + date);
-});
-
-balena.models.os.getLastModified('raspberrypi3', '2.0.0').then(function(date) {
- console.log('The raspberry-pi image was last modified in ' + date);
-});
-```
##### os.download(options) ⇒ Promise
diff --git a/package.json b/package.json
index 95dff00d7..22429301e 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"author": "Balena Ltd. ",
"license": "Apache-2.0",
"engines": {
- "node": ">=18.0"
+ "node": "^20.12.0 || >= 22.0.0"
},
"devDependencies": {
"@balena/env-parsing": "^1.2.0",
@@ -119,7 +119,7 @@
"dependencies": {
"@balena/es-version": "^1.0.0",
"@types/json-schema": "^7.0.9",
- "@types/node": "^18.19.50",
+ "@types/node": "^20.17.8",
"abortcontroller-polyfill": "^1.7.1",
"balena-auth": "^6.0.1",
"balena-errors": "^4.9.0",
diff --git a/src/models/api-key.ts b/src/models/api-key.ts
index 56a9eb33b..036f0703a 100644
--- a/src/models/api-key.ts
+++ b/src/models/api-key.ts
@@ -38,40 +38,49 @@ const getApiKeysModel = function (
*
* @description This method registers a new api key for the current user with the name given.
*
- * @param {String} name - the API key name
- * @param {String} [description=null] - the API key description
+ * @param {Object} createApiKeyParams - an object containing the parameters for the creation of an API key
+ * @param {String} createApiKeyParams.name - the API key name
+ * @param {String} createApiKeyParams.expiryDate - the API key expiry date
+ * @param {String} [createApiKeyParams.description=null] - the API key description
*
* @fulfil {String} - API key
* @returns {Promise}
*
* @example
- * balena.models.apiKey.create(apiKeyName).then(function(apiKey) {
+ * balena.models.apiKey.create({name: apiKeyName, expiryDate: 2030-10-12}).then(function(apiKey) {
* console.log(apiKey);
* });
*
* @example
- * balena.models.apiKey.create(apiKeyName, apiKeyDescription).then(function(apiKey) {
+ * balena.models.apiKey.create({name: apiKeyName, expiryDate: 2030-10-12, description: apiKeyDescription}).then(function(apiKey) {
* console.log(apiKey);
* });
*/
- async create(
- name: string,
- description: string | null = null,
- expiryDate: string | null = null,
- ): Promise {
+ async create({
+ name,
+ expiryDate,
+ description = null,
+ }: {
+ name: string;
+ expiryDate: string | null;
+ description?: string | null;
+ }): Promise {
+ if (typeof expiryDate === 'undefined') {
+ throw new Error(
+ 'An expiry date must be provided as either an ISO string or null',
+ );
+ }
const apiKeyBody: {
name: string;
+ expiryDate: string | null;
description?: string | null;
- expiryDate?: string | null;
} = {
name,
+ expiryDate,
};
if (typeof description === 'string' && !!description) {
apiKeyBody.description = description;
}
- if (typeof expiryDate === 'string' && !!expiryDate) {
- apiKeyBody.expiryDate = expiryDate;
- }
try {
const { body } = await request.send({
method: 'POST',
diff --git a/src/models/application-membership.ts b/src/models/application-membership.ts
index 39eaeb9f0..e791127ea 100644
--- a/src/models/application-membership.ts
+++ b/src/models/application-membership.ts
@@ -225,7 +225,7 @@ const getApplicationMembershipModel = function (
* @function
* @memberof balena.models.application.membership
*
- * @description This method adds a user to an application by their username.
+ * @description This method adds a user to an application by their username if they are a member of the organization.
*
* @param {Object} options - membership creation parameters
* @param {String|Number} options.application - application handle (string), or id (number)
@@ -248,9 +248,23 @@ const getApplicationMembershipModel = function (
PinePostResult
> {
const [{ id }, roleId] = await Promise.all([
- getApplication(application, { $select: 'id' }),
+ getApplication(application, {
+ $select: 'id',
+ $expand: {
+ organization: {
+ $select: 'id',
+ $filter: { organization_membership: { user: { username } } },
+ },
+ },
+ }),
roleName ? getRoleId(roleName) : undefined,
]);
+ // If the user does not have an organization membership, they cannot be added to an application
+ if (!id) {
+ throw new Error(
+ 'It is necessary that each user (Auth) that is member of an application that has an organization, is member of the organization',
+ );
+ }
type ApplicationMembershipBase = Omit;
type ApplicationMembershipPostBody = ApplicationMembershipBase & {
username: string;
diff --git a/src/models/application.ts b/src/models/application.ts
index cf5faa009..6c3f4c825 100644
--- a/src/models/application.ts
+++ b/src/models/application.ts
@@ -37,14 +37,7 @@ import * as url from 'url';
import once from 'lodash/once';
import * as errors from 'balena-errors';
-import {
- isId,
- isNoApplicationForKeyResponse,
- isNotFoundResponse,
- mergePineOptions,
- treatAsMissingApplication,
- withSupervisorLockedError,
-} from '../util';
+import { isId, mergePineOptions, withSupervisorLockedError } from '../util';
import {
getCurrentServiceDetailsPineExpand,
@@ -732,18 +725,15 @@ const getApplicationModel = function (
slugOrUuidOrIdOrIds: string | number | number[],
): Promise => {
if (typeof slugOrUuidOrIdOrIds === 'string') {
- try {
- const applicationId = await getId(slugOrUuidOrIdOrIds);
- await pine.delete({
- resource: 'application',
- id: applicationId,
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrIdOrIds, err);
- }
- throw err;
- }
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrIdOrIds, {
+ $select: 'id',
+ })
+ ).id;
+ await pine.delete({
+ resource: 'application',
+ id: applicationId,
+ });
return;
}
await batchApplicationOperation()({
@@ -783,21 +773,18 @@ const getApplicationModel = function (
slugOrUuidOrId: string | number,
newAppName: string,
): Promise => {
- try {
- const applicationId = await getId(slugOrUuidOrId);
- await pine.patch({
- resource: 'application',
- id: applicationId,
- body: {
- app_name: newAppName,
- },
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
+ await pine.patch({
+ resource: 'application',
+ id: applicationId,
+ body: {
+ app_name: newAppName,
+ },
+ });
},
/**
@@ -818,62 +805,19 @@ const getApplicationModel = function (
*/
restart: (slugOrUuidOrId: string | number): Promise =>
withSupervisorLockedError(async () => {
- try {
- const applicationId = await getId(slugOrUuidOrId);
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
- await request.send({
- method: 'POST',
- url: `/application/${applicationId}/restart`,
- baseUrl: apiUrl,
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ await request.send({
+ method: 'POST',
+ url: `/application/${applicationId}/restart`,
+ baseUrl: apiUrl,
+ });
}),
- /**
- * @summary Generate an API key for a specific application
- * @name generateApiKey
- * @public
- * @function
- * @memberof balena.models.application
- * @deprecated
- * @description
- * Generally you shouldn't use this method: if you're provisioning a recent BalenaOS
- * version (2.4.0+) then generateProvisioningKey should work just as well, but
- * be more secure.
- *
- * @param {String|Number} slugOrUuidOrId - application slug (string), uuid (string) or id (number)
- * @fulfil {String} - api key
- * @returns {Promise}
- *
- * @example
- * balena.models.application.generateApiKey('myorganization/myapp').then(function(apiKey) {
- * console.log(apiKey);
- * });
- *
- * @example
- * balena.models.application.generateApiKey(123).then(function(apiKey) {
- * console.log(apiKey);
- * });
- */
- generateApiKey: async (
- slugOrUuidOrId: string | number,
- ): Promise => {
- // Do a full get, not just getId, because the actual api endpoint doesn't fail if the id
- // doesn't exist. TODO: Can use getId once https://github.com/balena-io/balena-api/issues/110 is resolved
- const { id } = await exports.get(slugOrUuidOrId, { $select: 'id' });
- const { body } = await request.send({
- method: 'POST',
- url: `/application/${id}/generate-api-key`,
- baseUrl: apiUrl,
- });
- return body;
- },
-
/**
* @summary Generate a device provisioning key for a specific application
* @name generateProvisioningKey
@@ -881,56 +825,59 @@ const getApplicationModel = function (
* @function
* @memberof balena.models.application
*
- * @param {String|Number} slugOrUuidOrId - application slug (string), uuid (string) or id (number)
- * @param {String} [keyName] - Provisioning key name
- * @param {String} [keyDescription] - Description for provisioning key
- * @param {String} [keyExpiryDate] - Expiry Date for provisioning key
+ * @param {Object} generateProvisioningKeyParams - an object containing the parameters for the provisioning key generation
+ * @param {String|Number} generateProvisioningKeyParams.slugOrUuidOrId - application slug (string), uuid (string) or id (number)
+ * @param {String} generateProvisioningKeyParams.keyExpiryDate - Expiry Date for provisioning key
+ * @param {String} [generateProvisioningKeyParams.keyName] - Provisioning key name
+ * @param {String} [generateProvisioningKeyParams.keyDescription] - Description for provisioning key
* @fulfil {String} - device provisioning key
* @returns {Promise}
*
* @example
- * balena.models.application.generateProvisioningKey('myorganization/myapp').then(function(key) {
+ * balena.models.application.generateProvisioningKey({slugOrUuidOrId: 'myorganization/myapp', keyExpiryDate: '2030-10-12'}).then(function(key) {
* console.log(key);
* });
*
* @example
- * balena.models.application.generateProvisioningKey(123).then(function(key) {
+ * balena.models.application.generateProvisioningKey({slugOrUuidOrId: 123, keyExpiryDate: '2030-10-12'}).then(function(key) {
* console.log(key);
* });
*
* @example
- * balena.models.application.generateProvisioningKey(123, 'api key name', 'api key long description', '2030-01-01T00:00:00Z').then(function(key) {
+ * balena.models.application.generateProvisioningKey({slugOrUuidOrId: 123, keyExpiryDate: '2030-10-12', keyName: 'api key name', keyDescription: 'api key long description'}).then(function(key) {
* console.log(key);
* });
*/
- generateProvisioningKey: async (
- slugOrUuidOrId: string | number,
- keyName?: string,
- keyDescription?: string,
- keyExpiryDate?: string,
- ): Promise => {
- try {
- const applicationId = await getId(slugOrUuidOrId);
- const { body } = await request.send({
- method: 'POST',
- url: '/api-key/v1/',
- baseUrl: apiUrl,
- body: {
- actorType: 'application',
- actorTypeId: applicationId,
- roles: ['provisioning-api-key'],
- name: keyName,
- description: keyDescription,
- expiryDate: keyExpiryDate,
- },
- });
- return body;
- } catch (err) {
- if (isNoApplicationForKeyResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ generateProvisioningKey: async ({
+ slugOrUuidOrId,
+ keyExpiryDate,
+ keyName,
+ keyDescription,
+ }: {
+ slugOrUuidOrId: string | number;
+ keyExpiryDate: string | null;
+ keyName?: string;
+ keyDescription?: string;
+ }): Promise => {
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
+ const { body } = await request.send({
+ method: 'POST',
+ url: '/api-key/v2/',
+ baseUrl: apiUrl,
+ body: {
+ actorType: 'application',
+ actorTypeId: applicationId,
+ roles: ['provisioning-api-key'],
+ name: keyName,
+ description: keyDescription,
+ expiryDate: keyExpiryDate,
+ },
+ });
+ return body;
},
/**
@@ -1358,19 +1305,16 @@ const getApplicationModel = function (
);
}
- try {
- const applicationId = await getId(slugOrUuidOrId);
- await pine.patch({
- resource: 'application',
- id: applicationId,
- body: { is_accessible_by_support_until__date: expiryTimestamp },
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
+ await pine.patch({
+ resource: 'application',
+ id: applicationId,
+ body: { is_accessible_by_support_until__date: expiryTimestamp },
+ });
},
/**
@@ -1392,19 +1336,16 @@ const getApplicationModel = function (
revokeSupportAccess: async (
slugOrUuidOrId: string | number,
): Promise => {
- try {
- const applicationId = await getId(slugOrUuidOrId);
- await pine.patch({
- resource: 'application',
- id: applicationId,
- body: { is_accessible_by_support_until__date: null },
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
+ await pine.patch({
+ resource: 'application',
+ id: applicationId,
+ body: { is_accessible_by_support_until__date: null },
+ });
},
/**
diff --git a/src/models/config.ts b/src/models/config.ts
index d1a1fd650..83e1f1391 100644
--- a/src/models/config.ts
+++ b/src/models/config.ts
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as errors from 'balena-errors';
-import type { JSONSchema6 } from 'json-schema';
+import type { JSONSchema7 } from 'json-schema';
import type { InjectedDependenciesParam, InjectedOptionsParam } from '..';
import type * as DeviceTypeJson from '../types/device-type-json';
@@ -25,11 +25,6 @@ export interface Config {
deviceUrlsBase: string;
adminUrl: string;
gitServerUrl: string;
- /** @deprecated */
- pubnub?: {
- subscribe_key: string;
- publish_key: string;
- };
ga?: GaConfig;
mixpanelToken?: string;
intercomAppId?: string;
@@ -44,7 +39,7 @@ export interface Config {
supportedSocialProviders: string[];
}
-export type ConfigVarSchema = JSONSchema6 & {
+export type ConfigVarSchema = JSONSchema7 & {
will_reboot?: boolean;
warning?: string;
};
@@ -74,8 +69,13 @@ const getConfigModel = function (
const { request } = deps;
const { apiUrl } = opts;
+ // TODO: Drop when `instructions` is no longer returned by the `/config` and `/device-types/v1` endpoints
const normalizeDeviceTypes = (
- deviceTypes: DeviceTypeJson.DeviceType[],
+ deviceTypes: Array<
+ DeviceTypeJson.DeviceType & {
+ instructions?: string[] | DeviceTypeJson.DeviceTypeInstructions;
+ }
+ >,
): DeviceTypeJson.DeviceType[] =>
deviceTypes.map(function (deviceType) {
// Remove the device-type.json instructions to enforce
diff --git a/src/models/device.supervisor-api.partial.ts b/src/models/device.supervisor-api.partial.ts
index a98805d15..0cb01a6ca 100644
--- a/src/models/device.supervisor-api.partial.ts
+++ b/src/models/device.supervisor-api.partial.ts
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import * as bSemver from 'balena-semver';
import type {
InjectedOptionsParam,
InjectedDependenciesParam,
@@ -23,11 +22,7 @@ import type {
} from '..';
import type { Device } from '../types/models';
-import {
- isNotFoundResponse,
- treatAsMissingDevice,
- withSupervisorLockedError,
-} from '../util';
+import { withSupervisorLockedError } from '../util';
import { ensureVersionCompatibility } from '../util/device-os-version';
@@ -69,9 +64,6 @@ export const getSupervisorApiHelper = function (
} = deps;
const { apiUrl } = opts;
- const getId = (uuidOrId: string | number) =>
- sdkInstance.models.device._getId(uuidOrId);
-
const exports = {
/**
* @summary Ping a device
@@ -114,68 +106,6 @@ export const getSupervisorApiHelper = function (
});
},
- /**
- * @summary Get application container information
- * @name getApplicationInfo
- * @public
- * @function
- * @memberof balena.models.device
- *
- * @deprecated
- * @description
- * This is not supported on multicontainer devices, and will be removed in a future major release
- *
- * @param {String|Number} uuidOrId - device uuid (string) or id (number)
- * @fulfil {Object} - application info
- * @returns {Promise}
- *
- * @example
- * balena.models.device.getApplicationInfo('7cf02a6').then(function(appInfo) {
- * console.log(appInfo);
- * });
- *
- * @example
- * balena.models.device.getApplicationInfo(123).then(function(appInfo) {
- * console.log(appInfo);
- * });
- */
- getApplicationInfo: async (
- uuidOrId: string | number,
- ): Promise<{
- appId: string;
- commit: string;
- containerId: string;
- env: { [key: string]: string | number };
- imageId: string;
- }> => {
- const deviceOptions = {
- $select: ['id', 'supervisor_version'],
- $expand: { belongs_to__application: { $select: 'id' } },
- } satisfies PineOptions;
-
- const device = (await sdkInstance.models.device.get(
- uuidOrId,
- deviceOptions,
- )) as PineTypedResult;
- ensureVersionCompatibility(
- device.supervisor_version,
- MIN_SUPERVISOR_APPS_API,
- 'supervisor',
- );
- const appId = device.belongs_to__application[0].id;
- const { body } = await request.send({
- method: 'POST',
- url: `/supervisor/v1/apps/${appId}`,
- baseUrl: apiUrl,
- body: {
- deviceId: device.id,
- appId,
- method: 'GET',
- },
- });
- return body;
- },
-
/**
* @summary Identify device
* @name identify
@@ -228,133 +158,6 @@ export const getSupervisorApiHelper = function (
* balena.models.device.restartApplication(123);
*/
restartApplication: (uuidOrId: string | number): Promise =>
- withSupervisorLockedError(async () => {
- try {
- const deviceOptions = {
- $select: ['id', 'supervisor_version'],
- $expand: { belongs_to__application: { $select: 'id' } },
- } satisfies PineOptions;
- const device = (await sdkInstance.models.device.get(
- uuidOrId,
- deviceOptions,
- )) as PineTypedResult;
- // TODO: Drop this once we drop support for ResinOS v2.11.0.
- if (
- !bSemver.valid(device.supervisor_version) ||
- bSemver.lt(device.supervisor_version, '7.0.0')
- ) {
- return (
- await request.send({
- method: 'POST',
- url: `/device/${device.id}/restart`,
- baseUrl: apiUrl,
- timeout: CONTAINER_ACTION_ENDPOINT_TIMEOUT,
- })
- ).body;
- }
-
- const appId = device.belongs_to__application[0].id;
- const { body } = await request.send({
- method: 'POST',
- url: `/supervisor/v1/restart`,
- baseUrl: apiUrl,
- body: {
- deviceId: device.id,
- appId,
- data: {
- appId,
- },
- },
- timeout: CONTAINER_ACTION_ENDPOINT_TIMEOUT,
- });
- return body;
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingDevice(uuidOrId, err);
- }
- throw err;
- }
- }),
-
- /**
- * @summary Start application on device
- * @name startApplication
- * @public
- * @function
- * @memberof balena.models.device
- *
- * @deprecated
- * @description
- * This is not supported on multicontainer devices, and will be removed in a future major release
- *
- * @param {String|Number} uuidOrId - device uuid (string) or id (number)
- * @fulfil {String} - application container id
- * @returns {Promise}
- *
- * @example
- * balena.models.device.startApplication('7cf02a6').then(function(containerId) {
- * console.log(containerId);
- * });
- *
- * @example
- * balena.models.device.startApplication(123).then(function(containerId) {
- * console.log(containerId);
- * });
- */
- startApplication: async (uuidOrId: string | number): Promise => {
- const deviceOptions = {
- $select: ['id', 'supervisor_version'],
- $expand: { belongs_to__application: { $select: 'id' } },
- } satisfies PineOptions;
- const device = (await sdkInstance.models.device.get(
- uuidOrId,
- deviceOptions,
- )) as PineTypedResult;
- ensureVersionCompatibility(
- device.supervisor_version,
- MIN_SUPERVISOR_APPS_API,
- 'supervisor',
- );
- const appId = device.belongs_to__application[0].id;
- const { body } = await request.send({
- method: 'POST',
- url: `/supervisor/v1/apps/${appId}/start`,
- baseUrl: apiUrl,
- body: {
- deviceId: device.id,
- appId,
- },
- timeout: CONTAINER_ACTION_ENDPOINT_TIMEOUT,
- });
- return body.containerId;
- },
-
- /**
- * @summary Stop application on device
- * @name stopApplication
- * @public
- * @function
- * @memberof balena.models.device
- *
- * @deprecated
- * @description
- * This is not supported on multicontainer devices, and will be removed in a future major release
- *
- * @param {String|Number} uuidOrId - device uuid (string) or id (number)
- * @fulfil {String} - application container id
- * @returns {Promise}
- *
- * @example
- * balena.models.device.stopApplication('7cf02a6').then(function(containerId) {
- * console.log(containerId);
- * });
- *
- * @example
- * balena.models.device.stopApplication(123).then(function(containerId) {
- * console.log(containerId);
- * });
- */
- stopApplication: (uuidOrId: string | number): Promise =>
withSupervisorLockedError(async () => {
const deviceOptions = {
$select: ['id', 'supervisor_version'],
@@ -364,23 +167,22 @@ export const getSupervisorApiHelper = function (
uuidOrId,
deviceOptions,
)) as PineTypedResult;
- ensureVersionCompatibility(
- device.supervisor_version,
- MIN_SUPERVISOR_APPS_API,
- 'supervisor',
- );
+
const appId = device.belongs_to__application[0].id;
const { body } = await request.send({
method: 'POST',
- url: `/supervisor/v1/apps/${appId}/stop`,
+ url: `/supervisor/v1/restart`,
baseUrl: apiUrl,
body: {
deviceId: device.id,
appId,
+ data: {
+ appId,
+ },
},
timeout: CONTAINER_ACTION_ENDPOINT_TIMEOUT,
});
- return body.containerId;
+ return body;
}),
/**
@@ -410,26 +212,21 @@ export const getSupervisorApiHelper = function (
options = {};
}
- try {
- const deviceId = await getId(uuidOrId);
- const { body } = await request.send({
- method: 'POST',
- url: '/supervisor/v1/reboot',
- baseUrl: apiUrl,
- body: {
- deviceId,
- data: {
- force: Boolean(options?.force),
- },
+ const deviceId = (
+ await sdkInstance.models.device.get(uuidOrId, { $select: 'id' })
+ ).id;
+ const { body } = await request.send({
+ method: 'POST',
+ url: '/supervisor/v1/reboot',
+ baseUrl: apiUrl,
+ body: {
+ deviceId,
+ data: {
+ force: Boolean(options?.force),
},
- });
- return body;
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingDevice(uuidOrId, err);
- }
- throw err;
- }
+ },
+ });
+ return body;
}),
/**
diff --git a/src/models/device.ts b/src/models/device.ts
index 4ea9789ce..f27a12587 100644
--- a/src/models/device.ts
+++ b/src/models/device.ts
@@ -46,10 +46,8 @@ import memoizee from 'memoizee';
import {
isId,
- isNoDeviceForKeyResponse,
isFullUuid,
mergePineOptions,
- treatAsMissingDevice,
limitedMap,
groupByMap,
} from '../util';
@@ -222,17 +220,6 @@ const getDeviceModel = function (
return (await sdkInstance.models.config.getAll()).deviceUrlsBase;
});
- // Internal method for uuid/id disambiguation
- // Note that this throws an exception for missing uuids, but not missing ids
- const getId = async (uuidOrId: string | number) => {
- if (isId(uuidOrId)) {
- return uuidOrId;
- } else {
- const { id } = await exports.get(uuidOrId, { $select: 'id' });
- return id;
- }
- };
-
const getAppliedConfigVariableValue = async (
uuidOrId: string | number,
name: string,
@@ -345,7 +332,7 @@ const getDeviceModel = function (
async function startOsUpdate(
uuidOrUuids: string | string[],
targetOsVersion: string,
- options: { runDetached?: boolean } = { runDetached: false },
+ options: { runDetached?: boolean } = { runDetached: true },
): Promise> {
if (!targetOsVersion) {
throw new errors.BalenaInvalidParameterError(
@@ -433,7 +420,6 @@ const getDeviceModel = function (
}
const exports = {
- _getId: getId,
OverallStatus,
/**
* @summary Get Dashboard URL for a specific device
@@ -1385,9 +1371,12 @@ const getDeviceModel = function (
const [{ id: userId }, apiKey, application, deviceType] =
await Promise.all([
sdkInstance.auth.getUserInfo(),
- sdkInstance.models.application.generateProvisioningKey(
- applicationSlugOrUuidOrId,
- ),
+ sdkInstance.models.application.generateProvisioningKey({
+ slugOrUuidOrId: applicationSlugOrUuidOrId,
+ // Use 10 minute expiry date as we will immediately use the provisioning key to create a device and then not need it
+ keyExpiryDate: new Date(Date.now() + 1000 * 60 * 10).toISOString(),
+ keyDescription: 'Created by SDK to register a device',
+ }),
sdkInstance.models.application.get(
applicationSlugOrUuidOrId,
applicationOptions,
@@ -1456,25 +1445,20 @@ const getDeviceModel = function (
keyDescription?: string,
keyExpiryDate?: string,
): Promise => {
- try {
- const deviceId = await getId(uuidOrId);
- const { body } = await request.send({
- method: 'POST',
- url: `/api-key/device/${deviceId}/device-key`,
- baseUrl: apiUrl,
- body: {
- name: keyName,
- description: keyDescription,
- expiryDate: keyExpiryDate,
- },
- });
- return body;
- } catch (err) {
- if (isNoDeviceForKeyResponse(err)) {
- treatAsMissingDevice(uuidOrId, err);
- }
- throw err;
- }
+ const deviceId = (
+ await sdkInstance.models.device.get(uuidOrId, { $select: 'id' })
+ ).id;
+ const { body } = await request.send({
+ method: 'POST',
+ url: `/api-key/device/${deviceId}/device-key`,
+ baseUrl: apiUrl,
+ body: {
+ name: keyName,
+ description: keyDescription,
+ expiryDate: keyExpiryDate,
+ },
+ });
+ return body;
},
/**
@@ -2271,9 +2255,7 @@ const getDeviceModel = function (
* The version **must** be the exact version number, a "prod" variant and greater than the one running on the device.
* To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`.
* @param {Object} [options] - options
- * @param {Boolean} [options.runDetached] - run the update in detached mode.
- * Default behaviour is runDetached=false but is DEPRECATED and will be removed in a future release. Use runDetached=true
- * for more reliable updates.
+ * @param {Boolean} [options.runDetached] - run the update in detached mode. True by default
* @fulfil {Object} - action response
* @returns {Promise}
*
@@ -2284,41 +2266,6 @@ const getDeviceModel = function (
*/
startOsUpdate,
- /**
- * @deprecated
- * @summary Get the OS update status of a device. This will no longer return a useful status for runDetached=true updates.
- * @name getOsUpdateStatus
- * @public
- * @function
- * @memberof balena.models.device
- *
- * @param {String} uuid - full device uuid
- * @fulfil {Object} - action response
- * @returns {Promise}
- *
- * @example
- * balena.models.device.getOsUpdateStatus('7cf02a687b74206f92cb455969cf8e98').then(function(status) {
- * console.log(result.status);
- * });
- */
- getOsUpdateStatus: async (uuid: string): Promise => {
- try {
- const osUpdateHelper = await getOsUpdateHelper();
- return await osUpdateHelper.getOsUpdateStatus(uuid);
- } catch (err) {
- if (err.statusCode !== 400) {
- throw err;
- }
-
- // as an attempt to reduce the requests for this method
- // check whether the device exists only when the request rejects
- // so that it's rejected with the appropriate BalenaDeviceNotFound error
- await exports.get(uuid, { $select: 'id' });
- // if the device exists, then re-throw the original error
- throw err;
- }
- },
-
/**
* @namespace balena.models.device.tags
* @memberof balena.models.device
diff --git a/src/models/image.ts b/src/models/image.ts
index 5776e0df4..dd4196354 100644
--- a/src/models/image.ts
+++ b/src/models/image.ts
@@ -16,7 +16,6 @@ limitations under the License.
import * as errors from 'balena-errors';
import type { Image, PineOptions, InjectedDependenciesParam } from '..';
-import { mergePineOptions } from '../util';
const getImageModel = function (deps: InjectedDependenciesParam) {
const { pine } = deps;
@@ -39,33 +38,10 @@ const getImageModel = function (deps: InjectedDependenciesParam) {
* });
*/
async get(id: number, options: PineOptions = {}): Promise {
- const baseOptions = {
- $select: [
- // Select all the interesting fields *except* build_log
- // (which can be very large)
- 'id',
- 'content_hash',
- 'dockerfile',
- 'project_type',
- 'status',
- 'error_message',
- 'image_size',
- 'created_at',
- 'push_timestamp',
- 'start_timestamp',
- 'end_timestamp',
- ],
- } satisfies PineOptions;
const image = await pine.get({
resource: 'image',
id,
- options: mergePineOptions(
- baseOptions,
- options,
- // TODO: Mark the build_log as explicitRead in the next API model version
- // so that we can remove this & the explicit property selection from here.
- true,
- ),
+ options,
});
if (image == null) {
throw new errors.BalenaImageNotFound(id);
diff --git a/src/models/organization-membership.ts b/src/models/organization-membership.ts
index aa9267f30..c588a3dac 100644
--- a/src/models/organization-membership.ts
+++ b/src/models/organization-membership.ts
@@ -22,9 +22,7 @@ import type {
OrganizationMembershipRoles,
OrganizationMembershipTag,
PineOptions,
- PineSubmitBody,
InjectedDependenciesParam,
- PinePostResult,
} from '..';
import { mergePineOptions } from '../util';
@@ -241,58 +239,6 @@ const getOrganizationMembershipModel = function (
});
},
- /**
- * @summary Creates a new membership for an organization
- * @name create
- * @public
- * @function
- * @memberof balena.models.organization.membership
- *
- * @deprecated use balena.models.organization.invite.create instead
- * @description This method adds a user to an organization by their usename.
- * WARNING: This method is deprecated, use balena.models.organization.invite.create instead.
- *
- * @param {Object} options - membership creation parameters
- * @param {String|Number} options.organization - organization handle (string), or id (number)
- * @param {String} options.username - the username of the balena user that will become a member
- * @param {String} [options.roleName="member"] - the role name to be granted to the membership
- *
- * @fulfil {Object} - organization membership
- * @returns {Promise}
- *
- * @example
- * balena.models.organization.membership.create({ organization: "myorg", username: "user123", roleName: "member" }).then(function(membership) {
- * console.log(membership);
- * });
- */
- async create({
- organization,
- username,
- roleName,
- }: OrganizationMembershipCreationOptions): Promise<
- PinePostResult
- > {
- const [{ id }, roleId] = await Promise.all([
- getOrganization(organization, { $select: 'id' }),
- roleName ? getRoleId(roleName) : undefined,
- ]);
- type OrganizationMembershipBase = Omit;
- type OrganizationMembershipPostBody = OrganizationMembershipBase & {
- username: string;
- };
- const body: PineSubmitBody = {
- username,
- is_member_of__organization: id,
- };
- if (roleName) {
- body.organization_membership_role = roleId;
- }
- return (await pine.post({
- resource: RESOURCE,
- body,
- })) as PinePostResult;
- },
-
/**
* @summary Changes the role of an organization member
* @name changeRole
diff --git a/src/models/organization.ts b/src/models/organization.ts
index 269bedb9d..12bcbe2fa 100644
--- a/src/models/organization.ts
+++ b/src/models/organization.ts
@@ -18,18 +18,13 @@ import * as errors from 'balena-errors';
import type * as BalenaSdk from '..';
import type { InjectedDependenciesParam, InjectedOptionsParam } from '..';
-import {
- isId,
- isNotFoundResponse,
- mergePineOptions,
- treatAsMissingOrganization,
-} from '../util';
+import { isId, mergePineOptions } from '../util';
const getOrganizationModel = function (
deps: InjectedDependenciesParam,
opts: InjectedOptionsParam,
) {
- const { pine } = deps;
+ const { pine, sdkInstance } = deps;
/* eslint-disable @typescript-eslint/no-require-imports */
const membershipModel = (
@@ -41,11 +36,6 @@ const getOrganizationModel = function (
).default(deps, opts, (...args: Parameters) => get(...args));
/* eslint-enable @typescript-eslint/no-require-imports */
- const getId = async (handleOrId: string | number) => {
- const { id } = await get(handleOrId, { $select: 'id' });
- return id;
- };
-
/**
* @summary Creates a new organization
* @name create
@@ -186,18 +176,13 @@ const getOrganizationModel = function (
* balena.models.organization.remove(123);
*/
const remove = async function (handleOrId: string | number): Promise {
- try {
- const id = await getId(handleOrId);
- await pine.delete({
- resource: 'organization',
- id,
- });
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingOrganization(handleOrId, err);
- }
- throw err;
- }
+ const id = (
+ await sdkInstance.models.organization.get(handleOrId, { $select: 'id' })
+ ).id;
+ await pine.delete({
+ resource: 'organization',
+ id,
+ });
};
return {
diff --git a/src/models/os.ts b/src/models/os.ts
index 580f6530b..bf8444228 100644
--- a/src/models/os.ts
+++ b/src/models/os.ts
@@ -20,7 +20,6 @@ import once from 'lodash/once';
import {
isNotFoundResponse,
onlyIf,
- treatAsMissingApplication,
mergePineOptionsTyped,
type ExtendedPineTypedResult,
} from '../util';
@@ -38,6 +37,7 @@ import type {
PineTypedResult,
} from '..';
import { getAuthDependentMemoize } from '../util/cache';
+import { BalenaReleaseNotFound } from 'balena-errors';
const RELEASE_POLICY_TAG_NAME = 'release-policy';
const ESR_NEXT_TAG_NAME = 'esr-next';
@@ -76,8 +76,6 @@ export interface OsVersion
basedOnVersion?: string;
osType: string;
line?: OsLines;
- /** @deprecated */
- isRecommended?: boolean;
}
export interface ImgConfigOptions {
@@ -325,22 +323,6 @@ const getOsModel = function (
// transform version sets
Object.keys(osVersionsByDeviceType).forEach((deviceType) => {
osVersionsByDeviceType[deviceType].sort(sortVersions);
-
- // TODO: Drop in next major
- // Note: the recommended version settings might come from the server in the future, for now we just set it to the latest version for each os type.
- const recommendedPerOsType: Dictionary = {};
- osVersionsByDeviceType[deviceType].forEach((version) => {
- if (!recommendedPerOsType[version.osType]) {
- if (
- version.variant !== 'dev' &&
- !version.known_issue_list &&
- !bSemver.prerelease(version.raw_version)
- ) {
- version.isRecommended = true;
- recommendedPerOsType[version.osType] = true;
- }
- }
- });
});
return osVersionsByDeviceType;
@@ -544,19 +526,14 @@ const getOsModel = function (
*/
const _getMaxSatisfyingVersion = function (
versionOrRange: string,
- osVersions: Array>,
+ osVersions: Array>,
) {
- if (versionOrRange === 'recommended') {
- return osVersions.find((v) => v.isRecommended)?.raw_version;
- }
-
if (versionOrRange === 'latest') {
return osVersions[0]?.raw_version;
}
if (versionOrRange === 'default') {
- return (osVersions.find((v) => v.isRecommended) ?? osVersions[0])
- ?.raw_version;
+ return osVersions[0]?.raw_version;
}
const versions = osVersions.map((v) => v.raw_version);
@@ -643,56 +620,6 @@ const getOsModel = function (
return _getMaxSatisfyingVersion(versionOrRange, osVersions) ?? null;
};
- /**
- * @summary Get the OS image last modified date
- * @name getLastModified
- * @public
- * @function
- * @memberof balena.models.os
- *
- * @param {String} deviceType - device type slug
- * @param {String} [version] - semver-compatible version or 'latest', defaults to 'latest'.
- * Unsupported (unpublished) version will result in rejection.
- * The version **must** be the exact version number.
- * To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`.
- * @fulfil {Date} - last modified date
- * @returns {Promise}
- *
- * @example
- * balena.models.os.getLastModified('raspberry-pi').then(function(date) {
- * console.log('The raspberry-pi image was last modified in ' + date);
- * });
- *
- * balena.models.os.getLastModified('raspberrypi3', '2.0.0').then(function(date) {
- * console.log('The raspberry-pi image was last modified in ' + date);
- * });
- */
- const getLastModified = async function (
- deviceType: string,
- version = 'latest',
- ): Promise {
- try {
- deviceType = await _getNormalizedDeviceTypeSlug(deviceType);
- version = normalizeVersion(version);
- const response = await request.send({
- method: 'HEAD',
- url: '/download',
- qs: {
- deviceType,
- version,
- },
- baseUrl: apiUrl,
- });
- // TODO: Drop the ! on the next major
- return new Date(response.headers.get('last-modified')!);
- } catch (err) {
- if (isNotFoundResponse(err)) {
- throw new Error('No such version for the device type');
- }
- throw err;
- }
- };
-
/**
* @summary Download an OS image
* @name download
@@ -729,11 +656,15 @@ const getOsModel = function (
try {
const slug = await _getNormalizedDeviceTypeSlug(deviceType);
if (version === 'latest') {
- const versions = (await getAvailableOsVersions(slug)).filter(
+ const foundVersion = (await getAvailableOsVersions(slug)).find(
(v) => v.osType === OsTypes.DEFAULT,
);
- version = (versions.find((v) => v.isRecommended) ?? versions[0])
- ?.raw_version;
+ if (!foundVersion) {
+ throw new BalenaReleaseNotFound(
+ 'No version available for this device type',
+ );
+ }
+ version = foundVersion.raw_version;
} else {
version = normalizeVersion(version);
}
@@ -809,26 +740,22 @@ const getOsModel = function (
options.network = options.network ?? 'ethernet';
- try {
- const applicationId =
- await sdkInstance.models.application._getId(slugOrUuidOrId);
-
- const { body } = await request.send({
- method: 'POST',
- url: '/download-config',
- baseUrl: apiUrl,
- body: {
- ...options,
- appId: applicationId,
- },
- });
- return body;
- } catch (err) {
- if (isNotFoundResponse(err)) {
- treatAsMissingApplication(slugOrUuidOrId, err);
- }
- throw err;
- }
+ const applicationId = (
+ await sdkInstance.models.application.get(slugOrUuidOrId, {
+ $select: 'id',
+ })
+ ).id;
+
+ const { body } = await request.send({
+ method: 'POST',
+ url: '/download-config',
+ baseUrl: apiUrl,
+ body: {
+ ...options,
+ appId: applicationId,
+ },
+ });
+ return body;
};
/**
@@ -1125,7 +1052,6 @@ const getOsModel = function (
getAvailableOsVersions,
getMaxSatisfyingVersion,
getDownloadSize,
- getLastModified,
download,
getConfig,
isSupportedOsUpdate,
diff --git a/src/types/device-type-json.ts b/src/types/device-type-json.ts
index 89e9f2c3e..1ec90f078 100644
--- a/src/types/device-type-json.ts
+++ b/src/types/device-type-json.ts
@@ -14,8 +14,6 @@ export interface DeviceType {
isDependent?: boolean;
imageDownloadAlerts?: DeviceTypeDownloadAlert[];
- /** @deprecated Use the balena.models.deviceType.getInstructions() */
- instructions?: string[] | DeviceTypeInstructions;
gettingStartedLink?: string | DeviceTypeGettingStartedLink;
stateInstructions?: { [key: string]: string[] };
options?: DeviceTypeOptions[];
@@ -25,8 +23,6 @@ export interface DeviceType {
command: string;
}>;
};
- /** @deprecated Use the DeviceType.contract.data.led */
- supportsBlink?: boolean;
yocto: {
fstype?: string;
deployArtifact: string;
@@ -40,8 +36,6 @@ export interface DeviceType {
};
/** Holds the latest balenaOS version */
buildId?: string;
- /** @deprecated Use the logo field from the models.deviceType.get() method. */
- logoUrl?: string;
}
export interface DeviceTypeDownloadAlert {
diff --git a/src/types/jwt.ts b/src/types/jwt.ts
index 57dcabf70..1b81b4d79 100644
--- a/src/types/jwt.ts
+++ b/src/types/jwt.ts
@@ -5,45 +5,4 @@ export interface JWTUser {
actualUser?: number;
twoFactorRequired?: boolean;
permissions?: string[];
-
- /** @deprecated Use the user resource */
- created_at?: string;
- /** @deprecated Use the user resource */
- username?: string;
- /** @deprecated Use the actualUser field */
- loginAs?: boolean;
- /** @deprecated */
- features?: string[];
-
- /** @deprecated Use the user_profile resource */
- first_name?: string;
- /** @deprecated Use the user_profile resource */
- last_name?: string;
- /** @deprecated Use the user_profile resource */
- email?: string;
- /** @deprecated Use the user_profile resource */
- account_type?: string;
- /** @deprecated Use the user_profile resource */
- company?: string;
- /** @deprecated Use the user_profile resource */
- has_disabled_newsletter?: boolean;
- /** @deprecated Use the user_profile resource */
- hasPasswordSet?: boolean;
- /** @deprecated Use the user_profile resource */
- must_be_verified?: boolean;
- /** @deprecated Use the user_profile resource */
- is_verified?: boolean;
-
- /** @deprecated */
- intercomUserName?: string;
- /** @deprecated */
- intercomUserHash?: string;
-
- /** @deprecated Use the social_service_account resource */
- social_service_account?: SocialServiceAccount[];
-}
-
-interface SocialServiceAccount {
- provider: string;
- display_name: string;
}
diff --git a/src/types/models.ts b/src/types/models.ts
index 6c88e4316..a300c039a 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -284,8 +284,6 @@ export interface ApplicationType {
name: string;
slug: string;
description: string | null;
- /** @deprecated */
- supports_gateway_mode: boolean;
supports_multicontainer: boolean;
supports_web_url: boolean;
is_legacy: boolean;
@@ -356,8 +354,6 @@ export interface Release {
update_timestamp: string;
end_timestamp: string | null;
phase: 'next' | 'current' | 'sunset' | 'end-of-life' | null;
- /** @deprecated */
- release_version: string | null;
semver: string;
semver_major: number;
semver_minor: number;
@@ -379,8 +375,6 @@ export interface Release {
is_created_by__user: OptionalNavigationResource;
belongs_to__application: NavigationResource;
- /** @deprecated Prefer using the Term Form "release_image" property */
- contains__image?: ReverseNavigationResource;
release_image?: ReverseNavigationResource;
should_be_running_on__application?: ReverseNavigationResource;
is_running_on__device?: ReverseNavigationResource;
@@ -603,8 +597,6 @@ export interface ImageInstall {
status: string;
install_date: string;
- /** @deprecated Use `installs__image` instead. */
- image: NavigationResource;
installs__image: NavigationResource;
device: NavigationResource;
is_provided_by__release: NavigationResource;
diff --git a/src/util/device-actions/os-update/index.ts b/src/util/device-actions/os-update/index.ts
index 844e74fb0..11e29b2df 100644
--- a/src/util/device-actions/os-update/index.ts
+++ b/src/util/device-actions/os-update/index.ts
@@ -43,16 +43,7 @@ export const getOsUpdateHelper = function (
});
};
- const getOsUpdateStatus = (uuid: string) => {
- return deviceActionsService.getActionStatus({
- uuid,
- // TODO: this is an assumption recorded here: https://github.com/resin-io/resin-proxy/issues/51#issuecomment-274087973
- actionId: OS_UPDATE_ACTION_NAME,
- });
- };
-
return {
startOsUpdate,
- getOsUpdateStatus,
};
};
diff --git a/src/util/device-service-details.ts b/src/util/device-service-details.ts
index 9bdc55d1c..b2b28ffc8 100644
--- a/src/util/device-service-details.ts
+++ b/src/util/device-service-details.ts
@@ -41,7 +41,7 @@ export const getCurrentServiceDetailsPineExpand = (expandRelease: boolean) => {
},
},
$expand: {
- image: {
+ installs__image: {
$select: ['id'],
$expand: {
is_a_build_of__service: {
@@ -68,7 +68,7 @@ interface WithServiceName {
function getSingleInstallSummary(
rawData: ImageInstall,
): CurrentService & WithServiceName {
- const image = (rawData.image as Image[])[0];
+ const image = (rawData.installs__image as Image[])[0];
const service = (image.is_a_build_of__service as Service[])[0];
let releaseInfo: {
@@ -90,7 +90,6 @@ function getSingleInstallSummary(
const result: CurrentService &
Partial> &
- Partial> &
WithServiceName = {
...rawData,
service_id: service.id,
@@ -100,8 +99,6 @@ function getSingleInstallSummary(
...releaseInfo,
};
- // prefer over omit for performance reasons
- delete result.image;
if ('installs__image' in result) {
delete result.installs__image;
}
diff --git a/src/util/index.ts b/src/util/index.ts
index e4f9e6d33..d4b391bbf 100644
--- a/src/util/index.ts
+++ b/src/util/index.ts
@@ -52,38 +52,6 @@ export const isUnauthorizedResponse = (err: Error) =>
export const isNotFoundResponse = (err: Error) =>
isBalenaRequestErrorResponseWithCode(err, 404);
-export const isNoDeviceForKeyResponse = (err: Error) =>
- isBalenaRequestErrorResponseWithCode(err, 500) &&
- err.body === 'No device found to associate with the api key';
-
-export const isNoApplicationForKeyResponse = (err: Error) =>
- isBalenaRequestErrorResponseWithCode(err, 500) &&
- err.body === 'No application found to associate with the api key';
-
-export const treatAsMissingOrganization = (
- handleOrId: string | number,
- err: Error,
-) => {
- const replacementErr = new errors.BalenaOrganizationNotFound(handleOrId);
- replacementErr.stack = err.stack ?? '';
- throw replacementErr;
-};
-
-export const treatAsMissingApplication = (
- slugOrUuidOrId: string | number,
- err: Error,
-) => {
- const replacementErr = new errors.BalenaApplicationNotFound(slugOrUuidOrId);
- replacementErr.stack = err.stack ?? '';
- throw replacementErr;
-};
-
-export const treatAsMissingDevice = (uuidOrId: string | number, err: Error) => {
- const replacementErr = new errors.BalenaDeviceNotFound(uuidOrId);
- replacementErr.stack = err.stack ?? '';
- throw replacementErr;
-};
-
// TODO: Make it so that it also infers the extras param
export function mergePineOptionsTyped<
R extends object,
@@ -99,15 +67,6 @@ export type ExtendedPineTypedResult<
> = TBaseResult &
IfDefined>;
-const knownPineOptionKeys = new Set([
- '$top',
- '$skip',
- '$select',
- '$expand',
- '$filter',
- '$orderby',
-]);
-
const passthroughPineOptionKeys = ['$top', '$skip', '$orderby'] as const;
// Merging two sets of pine options sensibly is more complicated than it sounds.
@@ -122,33 +81,19 @@ const passthroughPineOptionKeys = ['$top', '$skip', '$orderby'] as const;
export function mergePineOptions<
R extends object,
TDefault extends Pine.ODataOptions,
->(
- defaults: TDefault,
- extras: Pine.ODataOptions | undefined,
- replace$selects?: boolean,
-): TDefault;
+>(defaults: TDefault, extras: Pine.ODataOptions | undefined): TDefault;
export function mergePineOptions(
defaults: Pine.ODataOptions,
extras: Pine.ODataOptions | undefined,
- replace$selects?: boolean,
): Pine.ODataOptions;
export function mergePineOptions(
defaults: Pine.ODataOptions,
extras: Pine.ODataOptions | undefined,
- replace$selects?: boolean,
): Pine.ODataOptions {
if (!extras) {
return defaults;
}
- // TOOD: Consider dropping in the next major
- const unknownPineOption = Object.keys(extras).find(
- (key) => !knownPineOptionKeys.has(key),
- );
- if (unknownPineOption != null) {
- throw new Error(`Unknown pine option: ${unknownPineOption}`);
- }
-
const result = { ...defaults };
if (extras.$select != null) {
@@ -160,9 +105,7 @@ export function mergePineOptions(
(extras.$select as '*')
: [extras.$select];
- if (replace$selects) {
- result.$select = extraSelect;
- } else if (extraSelect === '*') {
+ if (extraSelect === '*') {
result.$select = '*';
} else {
result.$select = [
@@ -193,11 +136,7 @@ export function mergePineOptions(
}
if (extras.$expand != null) {
- result.$expand = mergeExpandOptions(
- defaults.$expand,
- extras.$expand,
- replace$selects,
- );
+ result.$expand = mergeExpandOptions(defaults.$expand, extras.$expand);
}
return result;
@@ -206,7 +145,6 @@ export function mergePineOptions(
const mergeExpandOptions = (
defaultExpand: Pine.Expand | undefined,
extraExpand: Pine.Expand | undefined,
- replace$selects?: boolean,
): Pine.Expand | undefined => {
if (defaultExpand == null) {
return extraExpand;
@@ -222,7 +160,6 @@ const mergeExpandOptions = (
$defaultExpand[expandKey] = mergePineOptions(
$defaultExpand[expandKey] ?? {},
$extraExpand[expandKey],
- replace$selects,
);
}
@@ -257,21 +194,6 @@ const convertExpandToObject = (
);
}
- // Check the options in this object are the ones we know how to merge
- for (const expandKey of Object.keys(expandOption) as Array<
- keyof typeof expandOption
- >) {
- const expandRelationshipOptions = expandOption[expandKey];
-
- // TOOD: Consider dropping in the next major
- const unknownPineOption = Object.keys(expandRelationshipOptions ?? {}).find(
- (key) => !knownPineOptionKeys.has(key),
- );
- if (unknownPineOption != null) {
- throw new Error(`Unknown pine expand options: ${unknownPineOption}`);
- }
- }
-
if (cloneIfNeeded) {
return { ...expandOption };
}
diff --git a/tests/integration/auth.spec.ts b/tests/integration/auth.spec.ts
index a4a46ed58..7c58f0012 100644
--- a/tests/integration/auth.spec.ts
+++ b/tests/integration/auth.spec.ts
@@ -83,9 +83,10 @@ describe('SDK authentication', function () {
it('should be able to login with an API Key', async () => {
const token = await balena.auth.authenticate(credentials);
await balena.auth.loginWithToken(token);
- const apiKey = await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey`,
- );
+ const apiKey = await balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ });
await balena.auth.logout();
await balena.auth.loginWithToken(apiKey);
expect(await balena.auth.getToken()).to.be.a('string');
diff --git a/tests/integration/balena.spec.ts b/tests/integration/balena.spec.ts
index 941b0fb47..050c95031 100644
--- a/tests/integration/balena.spec.ts
+++ b/tests/integration/balena.spec.ts
@@ -162,7 +162,9 @@ describe('Balena SDK', function () {
},
});
- const promise = balena.models.device.reboot(999999);
+ const promise = balena.request.send({
+ url: 'example.com',
+ });
return expect(promise).to.be.rejected.then(() =>
expect(called).to.equal(
@@ -370,10 +372,11 @@ describe('Balena SDK', function () {
givenLoggedInUser(before);
before(async function () {
- const testApiKey = await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey`,
- 'apiKeyDescription',
- );
+ const testApiKey = await balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ description: 'apiKeyDescription',
+ });
this.testApiKey = testApiKey;
expect(this.testApiKey).to.be.a('string');
await balena.auth.logout();
diff --git a/tests/integration/models/api-key.spec.ts b/tests/integration/models/api-key.spec.ts
index a8d77fd63..eef5c324c 100644
--- a/tests/integration/models/api-key.spec.ts
+++ b/tests/integration/models/api-key.spec.ts
@@ -18,40 +18,34 @@ describe('API Key model', function () {
parallel('', function () {
it('should be able to create a new api key', async function () {
- const key = await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey`,
- );
- expect(key).to.be.a('string');
- });
-
- it('should be able to create a new api key with description', async function () {
- const key = await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey2`,
- 'apiKeyDescription',
- );
- expect(key).to.be.a('string');
- });
-
- it('should be able to create a new api key with expiry-date', async function () {
- const tomorrowDate = new Date(Date.now() + 86400000).toISOString(); // one day in future
- const key = await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKeyWithExpiry`,
- 'apiKeyDescription',
- tomorrowDate,
- );
+ const tomorrowDate = new Date(
+ Date.now() + 1000 * 60 * 60 * 24,
+ ).toISOString();
+ const key = await balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey`,
+ expiryDate: tomorrowDate,
+ });
expect(key).to.be.a('string');
-
const userKeys = await balena.models.apiKey.getAllNamedUserApiKeys();
expect(userKeys).to.be.an('array');
const userKeyWithExpiry = userKeys.filter(
- (elem) => elem.name === `${TEST_KEY_NAME_PREFIX}_apiKeyWithExpiry`,
+ (elem) => elem.name === `${TEST_KEY_NAME_PREFIX}_apiKey`,
);
expect(userKeyWithExpiry).to.not.be.empty;
expect(userKeyWithExpiry[0])
.to.have.property('expiry_date')
.to.be.equal(tomorrowDate);
});
+
+ it('should be able to create a new api key with description', async function () {
+ const key = await balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey2`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ description: 'apiKeyDescription',
+ });
+ expect(key).to.be.a('string');
+ });
});
});
@@ -74,11 +68,15 @@ describe('API Key model', function () {
describe('given two named api keys', function () {
before(() =>
Promise.all([
- balena.models.apiKey.create(`${TEST_KEY_NAME_PREFIX}_apiKey1`),
- balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey2`,
- 'apiKey2Description',
- ),
+ balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey1`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ }),
+ balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey2`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ description: 'apiKey2Description',
+ }),
]),
);
@@ -122,11 +120,15 @@ describe('API Key model', function () {
describe('given two named api keys', function () {
before(() =>
Promise.all([
- balena.models.apiKey.create(`${TEST_KEY_NAME_PREFIX}_apiKey1`),
- balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKey2`,
- 'apiKey2Description',
- ),
+ balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey1`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ }),
+ balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKey2`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ description: 'apiKey2Description',
+ }),
]),
);
@@ -169,18 +171,20 @@ describe('API Key model', function () {
] as const;
before(async function () {
- await balena.models.apiKey.create(
- `${TEST_KEY_NAME_PREFIX}_apiKeyToBeUpdated`,
- 'apiKeyDescriptionToBeUpdated',
- );
+ await balena.models.apiKey.create({
+ name: `${TEST_KEY_NAME_PREFIX}_apiKeyToBeUpdated`,
+ expiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ description: 'apiKeyDescriptionToBeUpdated',
+ });
const [apiKey] = await balena.models.apiKey.getAll({
$filter: { name: `${TEST_KEY_NAME_PREFIX}_apiKeyToBeUpdated` },
});
ctx.namedUserApiKey = apiKey;
- await balena.models.application.generateProvisioningKey(
- this.application.id,
- );
+ await balena.models.application.generateProvisioningKey({
+ slugOrUuidOrId: this.application.id,
+ keyExpiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ });
await balena.models.device.generateDeviceKey(this.device.id);
});
diff --git a/tests/integration/models/application.spec.ts b/tests/integration/models/application.spec.ts
index 23b96eea9..2e0a51343 100644
--- a/tests/integration/models/application.spec.ts
+++ b/tests/integration/models/application.spec.ts
@@ -593,35 +593,6 @@ describe('Application Model', function () {
});
});
- describe('balena.models.application.generateApiKey()', function () {
- applicationRetrievalFields.forEach((prop) => {
- it(`should be able to generate an API key by ${prop}`, async function () {
- const apiKey = await balena.models.application.generateApiKey(
- this.application[prop],
- );
-
- expect(apiKey).to.be.a('string');
- expect(apiKey).to.have.length(32);
- });
- });
-
- it('should be rejected if the application slug does not exist', function () {
- const promise = balena.models.application.generateApiKey(
- `${this.initialOrg.handle}/helloworldapp`,
- );
- return expect(promise).to.be.rejectedWith(
- `Application not found: ${this.initialOrg.handle}/helloworldapp`,
- );
- });
-
- it('should be rejected if the application id does not exist', function () {
- const promise = balena.models.application.generateApiKey(999999);
- return expect(promise).to.be.rejectedWith(
- 'Application not found: 999999',
- );
- });
- });
-
describe('balena.models.application.generateProvisioningKey()', function () {
const getProvisioningKeys = async function (appNameOrSlug, options?) {
const provisioningKeys =
@@ -638,7 +609,12 @@ describe('Application Model', function () {
applicationRetrievalFields.forEach((prop) => {
it(`should be able to generate a provisioning key by ${prop}`, function () {
return balena.models.application
- .generateProvisioningKey(this.application[prop])
+ .generateProvisioningKey({
+ slugOrUuidOrId: this.application[prop],
+ keyExpiryDate: new Date(
+ Date.now() + 1000 * 60 * 60,
+ ).toISOString(),
+ })
.then(function (key) {
expect(_.isString(key)).to.be.true;
return expect(key).to.have.length(32);
@@ -653,8 +629,13 @@ describe('Application Model', function () {
);
const key = await balena.models.application.generateProvisioningKey(
- this.application[prop],
- `key_${prop}`,
+ {
+ slugOrUuidOrId: this.application[prop],
+ keyExpiryDate: new Date(
+ Date.now() + 1000 * 60 * 60,
+ ).toISOString(),
+ keyName: `key_${prop}`,
+ },
);
expect(key).to.be.a('string');
@@ -682,9 +663,14 @@ describe('Application Model', function () {
);
const key = await balena.models.application.generateProvisioningKey(
- this.application[prop],
- `key_${prop}`,
- `Provisioning key generated with name key_${prop}`,
+ {
+ slugOrUuidOrId: this.application[prop],
+ keyExpiryDate: new Date(
+ Date.now() + 1000 * 60 * 60,
+ ).toISOString(),
+ keyName: `key_${prop}`,
+ keyDescription: `Provisioning key generated with name key_${prop}`,
+ },
);
expect(key).to.be.a('string');
@@ -711,11 +697,16 @@ describe('Application Model', function () {
this.application[prop],
);
+ const oneHourDate = new Date(
+ Date.now() + 1000 * 60 * 60,
+ ).toISOString();
const key = await balena.models.application.generateProvisioningKey(
- this.application[prop],
- `key_${prop}`,
- `Provisioning key generated with name key_${prop}`,
- '2030-01-01',
+ {
+ slugOrUuidOrId: this.application[prop],
+ keyExpiryDate: oneHourDate,
+ keyName: `key_${prop}`,
+ keyDescription: `Provisioning key generated with name key_${prop}`,
+ },
);
expect(key).to.be.a('string');
@@ -734,22 +725,25 @@ describe('Application Model', function () {
expect(provisionKeys[0]).to.have.property('expiry_date');
expect(provisionKeys[0])
.to.have.property('expiry_date')
- .to.be.equal('2030-01-01T00:00:00.000Z');
+ .to.be.equal(oneHourDate);
});
});
it('should be rejected if the application slug does not exist', function () {
- const promise = balena.models.application.generateProvisioningKey(
- `${this.initialOrg.handle}/helloworldapp`,
- );
+ const promise = balena.models.application.generateProvisioningKey({
+ slugOrUuidOrId: `${this.initialOrg.handle}/helloworldapp`,
+ keyExpiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ });
return expect(promise).to.be.rejectedWith(
`Application not found: ${this.initialOrg.handle}/helloworldapp`,
);
});
it('should be rejected if the application id does not exist', function () {
- const promise =
- balena.models.application.generateProvisioningKey(999999);
+ const promise = balena.models.application.generateProvisioningKey({
+ slugOrUuidOrId: 999999,
+ keyExpiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ });
return expect(promise).to.be.rejectedWith(
'Application not found: 999999',
);
@@ -1536,7 +1530,6 @@ describe('Application Model', function () {
expect(imageInstall)
.to.have.property('download_progress')
.that.is.oneOf([50, null]);
- expect(imageInstall).to.have.property('image').that.has.length(1);
if (expectCommit) {
expect(imageInstall)
.to.have.property('is_provided_by__release')
diff --git a/tests/integration/models/device.spec.ts b/tests/integration/models/device.spec.ts
index 0986b1c4f..6c2f20794 100644
--- a/tests/integration/models/device.spec.ts
+++ b/tests/integration/models/device.spec.ts
@@ -1548,26 +1548,6 @@ describe('Device Model', function () {
});
});
- describe('balena.models.device.getOsUpdateStatus()', function () {
- givenADevice(before);
-
- it('should be able to get the current OS update status', async function () {
- const status = await balena.models.device.getOsUpdateStatus(
- this.device.uuid,
- );
- return expect(status).to.deep.match({
- status: 'idle',
- });
- });
-
- it('should be rejected if the device does not exist', function () {
- const promise = balena.models.device.getOsUpdateStatus('asdfghjkl');
- return expect(promise).to.be.rejectedWith(
- 'Device not found: asdfghjkl',
- );
- });
- });
-
['single uuid', 'array of uuids'].forEach((paramType) => {
describe(`balena.models.device.startOsUpdate() called with ${paramType}`, function () {
givenADevice(before);
@@ -2368,7 +2348,6 @@ describe('Device Model', function () {
expect(imageInstall)
.to.have.property('download_progress')
.that.is.oneOf([50, null]);
- expect(imageInstall).to.have.property('image').that.has.length(1);
expect(imageInstall)
.to.have.property('is_provided_by__release')
.that.has.length(1);
diff --git a/tests/integration/models/organization-membership.spec.ts b/tests/integration/models/organization-membership.spec.ts
index 9557985e2..5525af814 100644
--- a/tests/integration/models/organization-membership.spec.ts
+++ b/tests/integration/models/organization-membership.spec.ts
@@ -7,7 +7,6 @@ import {
credentials,
givenInitialOrganization,
givenLoggedInUser,
- organizationRetrievalFields,
TEST_ORGANIZATION_NAME,
} from '../setup';
import type * as BalenaSdk from '../../..';
@@ -180,105 +179,11 @@ describe('Organization Membership Model', function () {
});
});
- describe('balena.models.organization.membership.create()', function () {
- before(function () {
- ctx = this;
- });
-
- parallel('[read operations]', function () {
- it(`should not be able to add a new member to the organization usign a wrong role name`, async function () {
- const promise = balena.models.organization.membership.create({
- organization: ctx.organization.id,
- username: credentials.member.username,
- // @ts-expect-error invalid value
- roleName: 'unknown role',
- });
- await expect(promise).to.be.rejected.and.eventually.have.property(
- 'code',
- 'BalenaOrganizationMembershipRoleNotFound',
- );
- });
-
- const randomOrdInfo = {
- id: Math.floor(Date.now() / 1000),
- handle: `random_sdk_test_org_handle_${Math.floor(Date.now() / 1000)}`,
- };
-
- organizationRetrievalFields.forEach((field) => {
- it(`should not be able to add a new member when using an not existing organization ${field}`, async function () {
- const promise = balena.models.organization.membership.create({
- organization: randomOrdInfo[field],
- username: credentials.member.username,
- roleName: 'member',
- });
- await expect(promise).to.be.rejected.and.eventually.have.property(
- 'code',
- 'BalenaOrganizationNotFound',
- );
- });
- });
- });
-
- describe('[mutating operations]', function () {
- let membership:
- | BalenaSdk.PinePostResult
- | undefined;
- afterEach(async function () {
- await balena.models.organization.membership.remove(membership!.id);
- });
- organizationRetrievalFields.forEach(function (field) {
- it(`should be able to add a new member to the organization by ${field}`, async function () {
- membership = await balena.models.organization.membership.create({
- organization: this.organization[field],
- username: credentials.member.username,
- });
-
- expect(membership)
- .to.be.an('object')
- .that.has.nested.property('organization_membership_role.__id')
- .that.equals(this.orgMemberRole.id);
- });
- });
-
- it(`should be able to add a new member to the organization without providing a role`, async function () {
- membership = await balena.models.organization.membership.create({
- organization: this.organization.id,
- username: credentials.member.username,
- });
-
- expect(membership)
- .to.be.an('object')
- .that.has.nested.property('organization_membership_role.__id')
- .that.equals(this.orgMemberRole.id);
- });
-
- (['member', 'administrator'] as const).forEach(function (roleName) {
- it(`should be able to add a new member to the organization with a given role [${roleName}]`, async function () {
- membership = await balena.models.organization.membership.create({
- organization: this.organization.id,
- username: credentials.member.username,
- roleName,
- });
-
- expect(membership)
- .to.be.an('object')
- .that.has.nested.property('organization_membership_role.__id')
- .that.equals(this.orgRoleMap[roleName].id);
- });
- });
- });
- });
-
- describe('given a member organization membership [contained scenario]', function () {
+ // TODO: re-add this test in the future, we need to add a second user that is a member of the organization from the start
+ describe.skip('given a member organization membership [contained scenario]', function () {
let membership:
| BalenaSdk.PinePostResult
| undefined;
- beforeEach(async function () {
- membership = await balena.models.organization.membership.create({
- organization: this.organization.id,
- username: credentials.member.username,
- });
- });
describe('balena.models.organization.membership.remove()', function () {
keyAlternatives.forEach(([title, keyGetter]) => {
@@ -297,7 +202,8 @@ describe('Organization Membership Model', function () {
});
});
- describe('given an organization with an administrator organization membership [contained scenario]', function () {
+ // TODO: re-add this test in the future, we need to add a second user that is a member of the organization from the start
+ describe.skip('given an organization with an administrator organization membership [contained scenario]', function () {
const testOrg1Name = `${TEST_ORGANIZATION_NAME}_org_member_tests_${Date.now()}`;
let testOrg: BalenaSdk.PinePostResult | undefined;
let membership:
@@ -308,11 +214,6 @@ describe('Organization Membership Model', function () {
testOrg = await balena.models.organization.create({
name: testOrg1Name,
});
- membership = await balena.models.organization.membership.create({
- organization: testOrg.id,
- username: credentials.member.username,
- roleName: 'administrator',
- });
});
after(async function () {
diff --git a/tests/integration/models/organizationInvite.spec.ts b/tests/integration/models/organizationInvite.spec.ts
index 331c3d3aa..e1dcc1bee 100644
--- a/tests/integration/models/organizationInvite.spec.ts
+++ b/tests/integration/models/organizationInvite.spec.ts
@@ -1,8 +1,16 @@
import { expect } from 'chai';
import parallel from 'mocha.parallel';
-import { balena, givenInitialOrganization, givenLoggedInUser } from '../setup';
-import { timeSuite } from '../../util';
-import { assertDeepMatchAndLength } from '../../util';
+import {
+ balena,
+ givenInitialOrganization,
+ givenLoggedInUser,
+ credentials,
+ organizationRetrievalFields,
+} from '../setup';
+import { timeSuite, assertDeepMatchAndLength } from '../../util';
+import type * as BalenaSdk from '../../..';
+// eslint-disable-next-line no-restricted-imports
+import * as _ from 'lodash';
const TEST_EMAIL = 'user.test@example.org';
const TEST_MESSAGE = 'Hey!, Join my org on balenaCloud';
const TEST_ROLE = 'member';
@@ -119,6 +127,96 @@ describe('Organization Invite Model', function () {
});
});
});
+
+ parallel('[read operations]', function () {
+ const randomOrdInfo = {
+ id: Math.floor(Date.now() / 1000),
+ handle: `random_sdk_test_org_handle_${Math.floor(Date.now() / 1000)}`,
+ };
+
+ for (const field of organizationRetrievalFields) {
+ it(`should not be able to invite a new member when using an not existing organization ${field}`, async function () {
+ const promise = balena.models.organization.invite.create(
+ randomOrdInfo[field],
+ {
+ invitee: credentials.member.email,
+ roleName: 'member',
+ },
+ );
+ await expect(promise).to.be.rejected.and.eventually.have.property(
+ 'code',
+ 'BalenaOrganizationNotFound',
+ );
+ });
+ }
+ });
+
+ describe('[mutating operations]', function () {
+ let membership:
+ | BalenaSdk.PinePostResult
+ | undefined;
+ before(async function () {
+ const roles = await balena.pine.get({
+ resource: 'organization_membership_role',
+ options: { $select: ['id', 'name'] },
+ });
+ this.orgRoleMap = _.keyBy(roles, 'name');
+ roles.forEach((role) => {
+ expect(role).to.be.an('object');
+ expect(role).to.have.property('id').that.is.a('number');
+ });
+ this.orgMemberRole = this.orgRoleMap['member'];
+ });
+ afterEach(async function () {
+ await balena.models.organization.invite.revoke(membership!.id);
+ });
+ for (const field of organizationRetrievalFields) {
+ it(`should be able to invite a new member to the organization by ${field}`, async function () {
+ membership = await balena.models.organization.invite.create(
+ this.organization[field],
+ {
+ invitee: credentials.member.email,
+ },
+ );
+
+ expect(membership)
+ .to.be.an('object')
+ .that.has.nested.property('organization_membership_role.__id')
+ .that.equals(this.orgMemberRole.id);
+ });
+ }
+
+ it(`should be able to invite a new member to the organization without providing a role`, async function () {
+ membership = await balena.models.organization.invite.create(
+ this.organization.id,
+ {
+ invitee: credentials.member.email,
+ },
+ );
+
+ expect(membership)
+ .to.be.an('object')
+ .that.has.nested.property('organization_membership_role.__id')
+ .that.equals(this.orgMemberRole.id);
+ });
+
+ (['member', 'administrator'] as const).forEach(function (roleName) {
+ it(`should be able to invite a new member to the organization with a given role [${roleName}]`, async function () {
+ membership = await balena.models.organization.invite.create(
+ this.organization.id,
+ {
+ invitee: credentials.member.email,
+ roleName,
+ },
+ );
+
+ expect(membership)
+ .to.be.an('object')
+ .that.has.nested.property('organization_membership_role.__id')
+ .that.equals(this.orgRoleMap[roleName].id);
+ });
+ });
+ });
});
describe('given a single organization invite [contained scenario]', function () {
diff --git a/tests/integration/models/os.spec.ts b/tests/integration/models/os.spec.ts
index 74959d9de..0eb382ce1 100644
--- a/tests/integration/models/os.spec.ts
+++ b/tests/integration/models/os.spec.ts
@@ -488,12 +488,6 @@ describe('OS model', function () {
);
});
- it("should support 'recommended'", () => {
- expect(
- _getMaxSatisfyingVersion('recommended', defaultOsVersions),
- ).to.equal('2.85.2+rev3.prod');
- });
-
it("should support 'default'", () => {
expect(_getMaxSatisfyingVersion('default', defaultOsVersions)).to.equal(
'2.85.2+rev3.prod',
@@ -712,47 +706,6 @@ describe('OS model', function () {
});
});
- describe('balena.models.os.getLastModified()', function () {
- parallel('given a valid device slug', function () {
- it('should eventually be a valid Date instance', async function () {
- const lastModified =
- await balena.models.os.getLastModified('raspberry-pi');
- expect(lastModified).to.be.an.instanceof(Date);
- });
-
- it('should eventually be a valid Date instance if passing a device type alias', async function () {
- const lastModified =
- await balena.models.os.getLastModified('raspberrypi');
- expect(lastModified).to.be.an.instanceof(Date);
- });
-
- it('should be able to query for a specific version', async function () {
- const lastModified = await balena.models.os.getLastModified(
- 'raspberrypi',
- '1.26.1',
- );
- expect(lastModified).to.be.an.instanceof(Date);
- });
-
- it('should be able to query for a version containing a plus', async function () {
- const lastModified = await balena.models.os.getLastModified(
- 'raspberrypi',
- '2.0.6+rev3.prod',
- );
- expect(lastModified).to.be.an.instanceof(Date);
- });
- });
-
- describe('given an invalid device slug', () => {
- it('should be rejected with an error message', async function () {
- const promise = balena.models.os.getLastModified('foo-bar-baz');
- await expect(promise).to.be.rejectedWith(
- 'Invalid device type: foo-bar-baz',
- );
- });
- });
- });
-
describe('balena.models.os.download()', function () {
if (IS_BROWSER) {
return;
diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts
index 3adf07d87..219017aef 100644
--- a/tests/integration/setup.ts
+++ b/tests/integration/setup.ts
@@ -361,9 +361,10 @@ export function givenLoggedInWithAnApplicationApiKey(
givenAnApplication(beforeFn);
beforeFn(async function () {
- const key = await balena.models.application.generateProvisioningKey(
- this.application.slug,
- );
+ const key = await balena.models.application.generateProvisioningKey({
+ slugOrUuidOrId: this.application.slug,
+ keyExpiryDate: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
+ });
await balena.auth.logout();
await balena.auth.loginWithToken(key);
});
diff --git a/tests/util.spec.ts b/tests/util.spec.ts
index 6b04e464e..1371901e9 100644
--- a/tests/util.spec.ts
+++ b/tests/util.spec.ts
@@ -124,18 +124,6 @@ describe('Pine option merging', function () {
});
});
- it('overrides $select params for expand options for the same relationship, if present', function () {
- const result = mergePineOptions(
- { $expand: { device: { $select: ['id'] } } },
- { $expand: { device: { $select: ['name'] } } },
- true,
- );
-
- return expect(result).to.deep.equal({
- $expand: { device: { $select: ['name'] } },
- });
- });
-
it('adds $filter params for expand options, if present', function () {
const result = mergePineOptions(
{ $expand: 'device' },
@@ -240,13 +228,6 @@ describe('Pine option merging', function () {
});
});
- it('rejects any unknown extra options', () => {
- // @ts-expect-error b/c it's not typed
- expect(() => mergePineOptions({}, { unknownKey: 'value' })).to.throw(
- 'Unknown pine option: unknownKey',
- );
- });
-
it('ignores any unknown default options', () => {
// @ts-expect-error b/c it's not typed
expect(() => mergePineOptions({ unknownKey: 'value' }, {})).not.to.throw();
diff --git a/typing_tests/pine-options.ts b/typing_tests/pine-options.ts
index 1a41585c4..0735cd93c 100644
--- a/typing_tests/pine-options.ts
+++ b/typing_tests/pine-options.ts
@@ -586,7 +586,6 @@ export const appOptionsEValid20: BalenaSdk.PineOptions =
type ReleaseExpandablePropsExpectation =
| 'is_created_by__user'
| 'belongs_to__application'
- | 'contains__image'
| 'release_image'
| 'should_be_running_on__application'
| 'should_operate__device'