diff --git a/README.md b/README.md index f146928..6e7c012 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Those classes allow to add an arbitrary number of additional properties as a lis C8yAlarm.registerAdditionalProperty(typeName: String, for type: C.Type) ``` -Each of the extensible objects contains a dictionary object holding instances of custom fragments. Use the custom fragment's key to access it's value. +Each of the extensible objects contains a dictionary object holding instances of custom fragments. Use the custom fragment's key to access its value. ### Working with errors diff --git a/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift b/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift index 0aed87b..4307f6d 100644 --- a/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift @@ -49,7 +49,7 @@ public class AlarmsApi: AdaptableApi { /// - pageSize: /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - resolved: - /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. + /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. Takes precedence over the `status` parameter. /// - severity: /// The severity of the alarm to search for. /// @@ -57,7 +57,7 @@ public class AlarmsApi: AdaptableApi { /// - source: /// The managed object ID to which the alarm is associated. /// - status: - /// The status of the alarm to search for. + /// The status of the alarm to search for. Should not be used when `resolved` parameter is provided. /// /// **ⓘ Note** If you query for multiple alarm statuses at once, comma-separate the values. /// - type: @@ -142,7 +142,7 @@ public class AlarmsApi: AdaptableApi { /// - dateTo: /// End date or date and time of the alarm occurrence. /// - resolved: - /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. + /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. Takes precedence over the `status` parameter. /// - severity: /// The severity of the alarm to search for. /// @@ -150,7 +150,7 @@ public class AlarmsApi: AdaptableApi { /// - source: /// The managed object ID to which the alarm is associated. /// - status: - /// The status of the alarm to search for. + /// The status of the alarm to search for. Should not be used when `resolved` parameter is provided. /// /// **ⓘ Note** If you query for multiple alarm statuses at once, comma-separate the values. /// - withSourceAssets: @@ -285,7 +285,7 @@ public class AlarmsApi: AdaptableApi { /// /// Remove alarm collections specified by query parameters. /// - /// > **⚠️ Important:** Note that it is possible to call this endpoint without providing any parameter - it will result in deleting all alarms and it is not recommended.Also note that DELETE requests are not synchronous. The response could be returned before the delete request has been completed. + /// > **⚠️ Important:** DELETE requires at least one of the following parameters: `source`, `dateFrom`, `dateTo`, `createdFrom`, `createdTo`.Also note that DELETE requests are not synchronous. The response could be returned before the delete request has been completed. /// /// > Tip: Required roles /// ROLE_ALARM_ADMIN @@ -309,7 +309,7 @@ public class AlarmsApi: AdaptableApi { /// - dateTo: /// End date or date and time of the alarm occurrence. /// - resolved: - /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. + /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. Takes precedence over the `status` parameter. /// - severity: /// The severity of the alarm to search for. /// @@ -317,7 +317,7 @@ public class AlarmsApi: AdaptableApi { /// - source: /// The managed object ID to which the alarm is associated. /// - status: - /// The status of the alarm to search for. + /// The status of the alarm to search for. Should not be used when `resolved` parameter is provided. /// /// **ⓘ Note** If you query for multiple alarm statuses at once, comma-separate the values. /// - type: @@ -483,7 +483,7 @@ public class AlarmsApi: AdaptableApi { /// - dateTo: /// End date or date and time of the alarm occurrence. /// - resolved: - /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. + /// When set to `true` only alarms with status CLEARED will be fetched, whereas `false` will fetch all alarms with status ACTIVE or ACKNOWLEDGED. Takes precedence over the `status` parameter. /// - severity: /// The severity of the alarm to search for. /// @@ -491,7 +491,7 @@ public class AlarmsApi: AdaptableApi { /// - source: /// The managed object ID to which the alarm is associated. /// - status: - /// The status of the alarm to search for. + /// The status of the alarm to search for. Should not be used when `resolved` parameter is provided. /// /// **ⓘ Note** If you query for multiple alarm statuses at once, comma-separate the values. /// - type: diff --git a/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift b/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift index dfc83c4..11dfe23 100644 --- a/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift @@ -15,7 +15,7 @@ import Combine /// /// For each tenant, Cumulocity IoT manages the subscribed applications and provides a number of applications of various types.In case you want to subscribe a tenant to an application using an API, you must use the application name in the argument (as name). /// -/// Refer to the tables in [Administration > Managing applications](https://cumulocity.com/guides/10.7.0/users-guide/administration#managing-applications) in the User guide for the respective application name to be used. +/// Refer to the tables in [Platform administration > Standard tenant administration > Managing the ecosystem > Managing applications](https://cumulocity.com/docs/standard-tenant/ecosystem/#managing-applications) in the Cumulocity IoT user documentation for the respective application name to be used. /// /// > **ⓘ Note** The Accept header should be provided in all POST/PUT requests, otherwise an empty response body will be returned. public class ApplicationsApi: AdaptableApi { @@ -263,10 +263,10 @@ public class ApplicationsApi: AdaptableApi { /// - Parameters: /// - id: /// Unique identifier of the application. - /// - force: - /// Force deletion by unsubscribing all tenants from the application first and then deleting the application itself. /// - xCumulocityProcessingMode: /// Used to explicitly control the processing mode of the request. See [Processing mode](#processing-mode) for more details. + /// - force: + /// Force deletion by unsubscribing all tenants from the application first and then deleting the application itself. public func deleteApplication(id: String, force: Bool? = nil, xCumulocityProcessingMode: String? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)") @@ -301,6 +301,8 @@ public class ApplicationsApi: AdaptableApi { /// /// If the target application is hosted and has an active version, the new application will have the active version with the same content. /// + /// If the original application is hosted with versions, then only one binary version is cloned. By default it is a version with the "latest" tag. You can also specify a target version directly by using exactly one of the query parameters `version` or `tag`. + /// /// /// > Tip: Required roles /// ROLE_APPLICATION_MANAGEMENT_ADMIN @@ -317,12 +319,18 @@ public class ApplicationsApi: AdaptableApi { /// Unique identifier of the application. /// - xCumulocityProcessingMode: /// Used to explicitly control the processing mode of the request. See [Processing mode](#processing-mode) for more details. - public func copyApplication(id: String, xCumulocityProcessingMode: String? = nil) -> AnyPublisher { + /// - version: + /// The version field of the application version. + /// - tag: + /// The tag of the application version. + public func copyApplication(id: String, version: String? = nil, tag: String? = nil, xCumulocityProcessingMode: String? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/clone") .set(httpMethod: "post") .add(header: "X-Cumulocity-Processing-Mode", value: xCumulocityProcessingMode) .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.application+json") + .add(queryItem: "version", value: version) + .add(queryItem: "tag", value: tag) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) diff --git a/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift b/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift index 92e82fd..d504223 100644 --- a/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift @@ -9,7 +9,7 @@ import Foundation import Combine -/// It is possible to store, retrieve and delete binaries for events. Each event can have one binary attached. +/// It is possible to store, retrieve and delete binaries for events. Each event can have only one binary attached. public class AttachmentsApi: AdaptableApi { /// Retrieve the attached file of a specific event @@ -76,7 +76,7 @@ public class AttachmentsApi: AdaptableApi { .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "put") .add(header: "Content-Type", value: "text/plain") - .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.event+json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") .set(httpBody: body) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { @@ -95,10 +95,10 @@ public class AttachmentsApi: AdaptableApi { /// Attach a file to a specific event /// - /// Upload a file (binary) as an attachment of a specific event by a given ID. - /// The size of the attachment is configurable, and the default size is 50 MiB. The default chunk size is 5MiB. + /// Upload a file (binary) as an attachment of a specific event by a given ID.The size of the attachment is configurable, and the default size is 50 MiB. The default chunk size is 5MiB. /// - /// After the file has been uploaded, the corresponding event will contain the fragment `c8y_IsBinary` similar to: + /// > **ⓘ Note** If there is a binary already attached to the event, the POST request results in a 409 error. + /// When the file has been uploaded, the corresponding event contains the fragment `c8y_IsBinary` similar to: /// /// ```json /// "c8y_IsBinary": { @@ -107,7 +107,7 @@ public class AttachmentsApi: AdaptableApi { /// "type": "text/plain" /// } /// ``` - /// When using `multipart/form-data` each value is sent as a block of data (body part), with a user agent-defined delimiter (`boundary`) separating each part. The keys are given in the `Content-Disposition` header of each part. + /// There are two request body schemas you can use for your POST requests.`text/plain` is preselected (see below).If you set it to `multipart/form-data` each value is sent as a block of data (body part), with a user agent-defined delimiter (`boundary`) separating each part.The keys are given in the `Content-Disposition` header of each part. /// /// ```http /// POST /event/events/{id}/binaries @@ -149,7 +149,7 @@ public class AttachmentsApi: AdaptableApi { .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "post") .add(header: "Content-Type", value: "text/plain") - .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.event+json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") .set(httpBody: body) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { @@ -168,10 +168,10 @@ public class AttachmentsApi: AdaptableApi { /// Attach a file to a specific event /// - /// Upload a file (binary) as an attachment of a specific event by a given ID. - /// The size of the attachment is configurable, and the default size is 50 MiB. The default chunk size is 5MiB. + /// Upload a file (binary) as an attachment of a specific event by a given ID.The size of the attachment is configurable, and the default size is 50 MiB. The default chunk size is 5MiB. /// - /// After the file has been uploaded, the corresponding event will contain the fragment `c8y_IsBinary` similar to: + /// > **ⓘ Note** If there is a binary already attached to the event, the POST request results in a 409 error. + /// When the file has been uploaded, the corresponding event contains the fragment `c8y_IsBinary` similar to: /// /// ```json /// "c8y_IsBinary": { @@ -180,7 +180,7 @@ public class AttachmentsApi: AdaptableApi { /// "type": "text/plain" /// } /// ``` - /// When using `multipart/form-data` each value is sent as a block of data (body part), with a user agent-defined delimiter (`boundary`) separating each part. The keys are given in the `Content-Disposition` header of each part. + /// There are two request body schemas you can use for your POST requests.`text/plain` is preselected (see below).If you set it to `multipart/form-data` each value is sent as a block of data (body part), with a user agent-defined delimiter (`boundary`) separating each part.The keys are given in the `Content-Disposition` header of each part. /// /// ```http /// POST /event/events/{id}/binaries @@ -235,7 +235,7 @@ public class AttachmentsApi: AdaptableApi { .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "post") .add(header: "Content-Type", value: "multipart/form-data") - .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.event+json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") .add(header: "Content-Type", value: multipartBuilder.contentType) .set(httpBody: multipartBuilder.build()) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in diff --git a/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift b/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift index b2317c8..1b2cde3 100644 --- a/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift @@ -37,9 +37,9 @@ public class AuditsApi: AdaptableApi { /// - currentPage: /// The current page of the paginated results. /// - dateFrom: - /// Start date or date and time of the audit record. + /// Start date or date and time of the audit record (device time). /// - dateTo: - /// End date or date and time of the audit record. + /// End date or date and time of the audit record (device time). /// - pageSize: /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - source: diff --git a/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift b/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift index c2d09cf..989e99d 100644 --- a/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift @@ -11,12 +11,12 @@ import Combine /// Managed objects can perform operations to store, retrieve and delete binaries. One binary can store only one file. Together with the binary, a managed object is created which acts as a metadata information for the binary. /// -/// > **ⓘ Note** The Accept header should be provided in all POST/PUT requests, otherwise an empty response body will be returned. +/// > **ⓘ Note** Supports only HTTP 1.1 clients.**ⓘ Note** The Accept header should be provided in all POST/PUT requests, otherwise an empty response body will be returned. public class BinariesApi: AdaptableApi { - /// Retrieve the stored files + /// Search for stored files /// - /// Retrieve the stored files as a collections of managed objects. + /// Retrieve metadata information about stored files. Search for files by query parameters. This will not download the files. /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -42,7 +42,7 @@ public class BinariesApi: AdaptableApi { /// - pageSize: /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - text: - /// Search for managed objects where any property value is equal to the given one. Only string values are supported. + /// Search for managed objects where a property value is equal to the given one.The following properties are examined: `id, type, name, owner, externalIds`. /// - type: /// The type of managed object to search for. /// - withTotalPages: @@ -88,7 +88,7 @@ public class BinariesApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ROLE_INVENTORY_CREATE + /// ROLE_INVENTORY_ADMIN *OR* ROLE_INVENTORY_CREATE *OR* ROLE_BINARY_ADMIN *OR* ROLE_BINARY_CREATE /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -139,11 +139,11 @@ public class BinariesApi: AdaptableApi { /// Retrieve a stored file /// - /// Retrieve a stored file (managed object) by a given ID. + /// Retrieve a stored file (managed object) by a given ID.Supports chunk download and resuming an interrupted download using the [`Range` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range). /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the resource *OR* MANAGE_OBJECT_READ permission on the resource + /// ROLE_INVENTORY_READ *OR* ROLE_BINARY_READ *OR* owner of the resource *OR* MANAGE_OBJECT_READ permission on the resource /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -180,7 +180,7 @@ public class BinariesApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the resource *OR* MANAGE_OBJECT_ADMIN permission on the resource + /// ROLE_INVENTORY_ADMIN *OR* ROLE_BINARY_ADMIN *OR* owner of the resource *OR* MANAGE_OBJECT_ADMIN permission on the resource /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -221,7 +221,7 @@ public class BinariesApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the resource *OR* MANAGE_OBJECT_ADMIN permission on the resource + /// ROLE_INVENTORY_ADMIN *OR* ROLE_BINARY_ADMIN *OR* owner of the resource *OR* MANAGE_OBJECT_ADMIN permission on the resource /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: diff --git a/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift b/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift index d6ce31c..f2ca5d0 100644 --- a/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift @@ -20,7 +20,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -84,7 +84,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -140,7 +140,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -196,7 +196,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -221,6 +221,7 @@ public class ChildOperationsApi: AdaptableApi { requestBody.childAssets = nil requestBody.creationTime = nil requestBody.childAdditions = nil + requestBody.c8yLatestMeasurements = nil requestBody.`self` = nil requestBody.assetParents = nil requestBody.deviceParents = nil @@ -259,7 +260,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -312,7 +313,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -353,7 +354,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -397,7 +398,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -461,7 +462,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -517,7 +518,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -573,7 +574,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -598,6 +599,7 @@ public class ChildOperationsApi: AdaptableApi { requestBody.childAssets = nil requestBody.creationTime = nil requestBody.childAdditions = nil + requestBody.c8yLatestMeasurements = nil requestBody.`self` = nil requestBody.assetParents = nil requestBody.deviceParents = nil @@ -636,7 +638,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -689,7 +691,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -730,7 +732,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -774,7 +776,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -838,7 +840,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -894,7 +896,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -950,7 +952,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ((owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source) *AND* (owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the child)) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -975,6 +977,7 @@ public class ChildOperationsApi: AdaptableApi { requestBody.childAssets = nil requestBody.creationTime = nil requestBody.childAdditions = nil + requestBody.c8yLatestMeasurements = nil requestBody.`self` = nil requestBody.assetParents = nil requestBody.deviceParents = nil @@ -1013,7 +1016,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -1066,7 +1069,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* MANAGE_OBJECT_READ permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -1107,7 +1110,7 @@ public class ChildOperationsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source (parent) *OR* owner of the child *OR* MANAGE_OBJECT_ADMIN permission on the source (parent) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: diff --git a/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift b/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift index 5dea9f5..2d663a8 100644 --- a/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift @@ -22,6 +22,8 @@ public class CurrentUserApi: AdaptableApi { /// > Tip: Required roles /// ROLE_USER_MANAGEMENT_OWN_READ *OR* ROLE_SYSTEM /// + /// Users with ROLE_SYSTEM are not allowed to query with Accept header `application/vnd.com.nsn.cumulocity.user+json` + /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: /// @@ -31,7 +33,7 @@ public class CurrentUserApi: AdaptableApi { let builder = URLRequestBuilder() .set(resourcePath: "/user/currentUser") .set(httpMethod: "get") - .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.currentuser+json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.currentuser+json, application/vnd.com.nsn.cumulocity.user+json") return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) @@ -73,7 +75,6 @@ public class CurrentUserApi: AdaptableApi { requestBody.id = nil requestBody.lastPasswordChange = nil requestBody.twoFactorAuthenticationEnabled = nil - requestBody.devicePermissions = nil var encodedRequestBody: Data? = nil do { encodedRequestBody = try JSONEncoder().encode(requestBody) @@ -153,7 +154,7 @@ public class CurrentUserApi: AdaptableApi { /// /// Generate a secret code to create a QR code to set up the two-factor authentication functionality using a TFA app/service. /// - /// For more information about the feature, see [User Guide > Administration > Two-factor authentication](https://cumulocity.com/guides/users-guide/administration/#tfa) in the *Cumulocity IoT documentation*. + /// For more information about the feature, see [Platform administration > Authentication > Two-factor authentication](https://cumulocity.com/docs/authentication/tfa/) in the Cumulocity IoT user documentation. /// /// /// > Tip: Required roles @@ -222,7 +223,7 @@ public class CurrentUserApi: AdaptableApi { /// /// Activates or deactivates the two-factor authentication feature for the current user. /// - /// For more information about the feature, see [User Guide > Administration > Two-factor authentication](https://cumulocity.com/guides/users-guide/administration/#tfa) in the *Cumulocity IoT documentation*. + /// For more information about the feature, see [Platform administration > Authentication > Two-factor authentication](https://cumulocity.com/docs/authentication/tfa/) in the Cumulocity IoT user documentation. /// /// /// > Tip: Required roles diff --git a/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift b/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift index cf9bfa9..8349464 100644 --- a/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift @@ -11,7 +11,7 @@ import Combine /// API methods to create device credentials in Cumulocity IoT. /// -/// Device credentials can be enquired by devices that do not have credentials for accessing a tenant yet.Since the device does not have credentials yet, a set of fixed credentials is used for this API.The credentials can be obtained by [contacting support](https://cumulocity.com/guides/about-doc/contacting-support/). +/// Device credentials can be enquired by devices that do not have credentials for accessing a tenant yet.Since the device does not have credentials yet, a set of fixed credentials is used for this API.The credentials can be obtained by [contacting support](https://cumulocity.com/docs/additional-resources/contacting-support/). /// /// > **⚠️ Important:** Do not use your tenant credentials with this API. /// > **ⓘ Note** The Accept header should be provided in all POST requests, otherwise an empty response body will be returned. @@ -85,7 +85,7 @@ public class DeviceCredentialsApi: AdaptableApi { /// A separator is automatically obtained from the CSV file. Valid separator values are: `\t` (tabulation mark), `;` (semicolon) and `,` (comma). /// /// > **⚠️ Important:** The CSV file needs the "com_cumulocity_model_Agent.active" header with a value of "true" to be added to the request. - /// > **ⓘ Note** A bulk registration creates an elementary representation of the device. Then, the device needs to update it to a full representation with its own status. The device is ready to use only after it is updated to the full representation. Also see [credentials upload](https://cumulocity.com/guides/users-guide/device-management/#creds-upload) and [device integration](https://cumulocity.com/guides/device-sdk/rest/#device-integration). + /// > **ⓘ Note** A bulk registration creates an elementary representation of the device. Then, the device needs to update it to a full representation with its own status. The device is ready to use only after it is updated to the full representation. Also see [Device management > Device management application > Registering devices > Single device registration > Security token policy](https://cumulocity.com/docs/device-management-application/registering-devices/#security-token-policy) and [Device management > Device integration > Device integration using REST > Integration life cycle](https://cumulocity.com/docs/device-integration/rest/#integration-life-cycle). /// A CSV file can appear in many forms (with regard to the optional tenant column and the occurrence of device information): /// /// * If a user is logged in as the management tenant, then the columns ID, CREDENTIALS and TENANT are mandatory, and the device credentials will be created for the tenant mentioned in the TENANT column. @@ -110,13 +110,13 @@ public class DeviceCredentialsApi: AdaptableApi { /// /// ```csv /// ID;CREDENTIALS;TYPE;NAME;ICCID;IDTYPE;PATH;SHELL - /// id_101;abcd1234;type_of_device;Device 101;111111111;;csv device/subgroup0;1 - /// id_102;abcd1234;type_of_device;Device 102;222222222;;csv device/subgroup0;0 - /// id_111;abcd1234;type_of_device;Device 111;333333333;c8y_Imei;csv device1/subgroup1;0 - /// id_112;abcd1234;type_of_device;Device 112;444444444;;csv device1/subgroup1;1 - /// id_121;abcd1234;type_of_device;Device 121;555555555;;csv device1/subgroup2;1 - /// id_122;abcd1234;type_of_device;Device 122;;;csv device1/subgroup2; - /// id_131;abcd1234;type_of_device;Device 131;;;csv device1/subgroup3; + /// id_101;AbcD1234!1234AbcD;type_of_device;Device 101;111111111;;csv device/subgroup0;1 + /// id_102;AbcD1234!1234AbcD;type_of_device;Device 102;222222222;;csv device/subgroup0;0 + /// id_111;AbcD1234!1234AbcD;type_of_device;Device 111;333333333;c8y_Imei;csv device1/subgroup1;0 + /// id_112;AbcD1234!1234AbcD;type_of_device;Device 112;444444444;;csv device1/subgroup1;1 + /// id_121;AbcD1234!1234AbcD;type_of_device;Device 121;555555555;;csv device1/subgroup2;1 + /// id_122;AbcD1234!1234AbcD;type_of_device;Device 122;;;csv device1/subgroup2; + /// id_131;AbcD1234!1234AbcD;type_of_device;Device 131;;;csv device1/subgroup3; /// ``` /// There is also a simple registration method that creates all registration requests at once, then each one needs to go through regular acceptance.This simple registration only makes use of the ID and PATH fields from the list above. /// diff --git a/Sources/CumulocityCoreLibrary/Api/DevicePermissionsApi.swift b/Sources/CumulocityCoreLibrary/Api/DevicePermissionsApi.swift new file mode 100644 index 0000000..50e9a06 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Api/DevicePermissionsApi.swift @@ -0,0 +1,118 @@ +// +// DevicePermissionsApi.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation +import Combine + +/// API methods to retrieve and update device permissions assignments. +/// +/// Device permissions enable users to access and manipulate devices. +/// +/// The device permission structure is **[API:fragment_name:permission]** where: +/// +/// * **API** is one of the following values: OPERATION, ALARM, AUDIT, EVENT, MANAGED_OBJECT, MEASUREMENT or "*" +/// * **fragment_name** can be the name of any fragment, for example, "c8y_Restart" or "*" +/// * **permission** is ADMIN, READ or "*" +/// +/// Required permission per HTTP method: +/// +/// * GET - READ or "*" +/// * PUT - ADMIN or "*" +/// +/// The wildcard "*" enables you to access every API and stored object regardless of the fragments that are inside it. +/// +/// > **⚠️ Important:** If there is no fragment in an object, for example, to read the object, you must use the wildcard "*" for the **fragment_name** part of the device permission (see the structure above). For example: `"10200":["MEASUREMENT:*:READ"]`. +public class DevicePermissionsApi: AdaptableApi { + + /// Returns all device permissions assignments + /// + /// Returns all device permissions assignments if the current user has READ permission. + /// + /// + /// > Tip: Required roles + /// ROLE_USER_MANAGEMENT_READ *OR* ROLE_USER_MANAGEMENT_CREATE + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The request has succeeded and the device permissions assignments are sent in the response. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not authorized to perform this operation. + /// + /// - Parameters: + /// - id: + /// Unique identifier of the managed object. + public func getDevicePermissionAssignments(id: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/user/devicePermissions/\(id)") + .set(httpMethod: "get") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).decode(type: C8yDevicePermissionOwners.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Updates the device permissions assignments + /// + /// Updates the device permissions assignments if the current user has ADMIN permission or CREATE permission and also has all device permissions. + /// + /// + /// > Tip: Required roles + /// ROLE_USER_MANAGEMENT_ADMIN *OR* ROLE_USER_MANAGEMENT_CREATE + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The device permissions were successfully updated. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not authorized to perform this operation. + /// + /// - Parameters: + /// - body: + /// + /// - id: + /// Unique identifier of the managed object. + public func updateDevicePermissionAssignments(body: C8yUpdatedDevicePermissions, id: String) -> AnyPublisher { + let requestBody = body + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/user/devicePermissions/\(id)") + .set(httpMethod: "put") + .add(header: "Content-Type", value: "application/json") + .add(header: "Accept", value: "application/json") + .set(httpBody: encodedRequestBody) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } +} diff --git a/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift b/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift index 736cd15..3485cec 100644 --- a/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift @@ -37,6 +37,13 @@ import Combine /// /// > Tip: When I have a device with children are the requests counted always to the root device or separately for each child? /// Separately for each child. +/// +/// > Tip: Why do device statistics show significantly smaller request numbers than the total number of created and updated request from usage statistics? +/// The important aspect here is the moment of recording values for the counters. For inbound data usage statistics we count every request that passed authorization, including invalid requests, as stated in usage statistics description. +/// +/// For device statistics it is different. We count requests after data is successfully stored in the database (or transient), which means the request was valid and there was no problem with persistence. +/// +/// In summary, if you observe that your usage statistics counters are significantly larger than your device statistics counters, there is a good chance that some devices or microservices in your tenant are constantly sending invalid requests. In such a situation, the client should check the state of theirs tenant. public class DeviceStatisticsApi: AdaptableApi { /// Retrieve monthly device statistics diff --git a/Sources/CumulocityCoreLibrary/Api/EventsApi.swift b/Sources/CumulocityCoreLibrary/Api/EventsApi.swift index 1f4aeb6..689371a 100644 --- a/Sources/CumulocityCoreLibrary/Api/EventsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/EventsApi.swift @@ -172,7 +172,7 @@ public class EventsApi: AdaptableApi { /// /// DELETE requests are not synchronous. The response could be returned before the delete request has been completed. This may happen especially when the deleted event has a lot of associated data. After sending the request, the platform starts deleting the associated data in an asynchronous way. Finally, the requested event is deleted after all associated data has been deleted. /// - /// > **⚠️ Important:** Note that it is possible to call this endpoint without providing any parameter - it will result in deleting all events and it is not recommended. + /// > **⚠️ Important:** DELETE requires at least one of the following parameters: `source`, `dateFrom`, `dateTo`, `createdFrom`, `createdTo`. /// /// > Tip: Required roles /// ROLE_EVENT_ADMIN diff --git a/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift b/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift index 0ae8b62..82619e6 100644 --- a/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift @@ -64,6 +64,7 @@ public class ExternalIDsApi: AdaptableApi { /// /// * HTTP 201 An external ID was created. /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 404 Global ID not found. /// * HTTP 409 Duplicate – Identity already bound to a different Global ID. /// /// - Parameters: diff --git a/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift index 61a0e25..2a63c8b 100644 --- a/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift @@ -11,14 +11,14 @@ import Combine /// API methods to retrieve the login options configured in the tenant. /// -/// More detailed information about the parameters and their meaning can be found in [Administration > Changing settings](https://cumulocity.com/guides/users-guide/administration/#changing-settings) in the *Users guide*. +/// More detailed information about the parameters and their meaning can be found in [Platform administration > Standard tenant administration > Changing settings](https://cumulocity.com/docs/standard-tenant/changing-settings/) in the Cumulocity IoT user documentation. /// /// > **ⓘ Note** If OAuth external is the only login option shown in the response, the user will be automatically redirected to the SSO login screen. public class LoginOptionsApi: AdaptableApi { - /// Retrieve the login options + /// Retrieve all login options /// - /// Retrieve the login options available in the tenant. + /// Retrieve all login options available in the tenant. /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -77,6 +77,7 @@ public class LoginOptionsApi: AdaptableApi { public func createLoginOption(body: C8yAuthConfig) -> AnyPublisher { var requestBody = body requestBody.`self` = nil + requestBody.id = nil var encodedRequestBody: Data? = nil do { encodedRequestBody = try JSONEncoder().encode(requestBody) @@ -104,6 +105,138 @@ public class LoginOptionsApi: AdaptableApi { }).decode(type: C8yAuthConfig.self, decoder: JSONDecoder()).eraseToAnyPublisher() } + /// Retrieve a specific login option + /// + /// Retrieve a specific login option in the tenant by the given type or ID. + /// + /// + /// > Tip: Required roles + /// ((ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_USER_MANAGEMENT_OWN_ADMIN *OR* ROLE_USER_MANAGEMENT_CREATE) *AND* tenant access to login option is not restricted by management tenant) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The request has succeeded and the login option is sent in the response. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not authorized to perform this operation. + /// * HTTP 404 Login option not found. + /// + /// - Parameters: + /// - typeOrId: + /// The type or ID of the login option. The type's value is case insensitive and can be `OAUTH2`, `OAUTH2_INTERNAL` or `BASIC`. + public func getLoginOption(typeOrId: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/loginOptions/\(typeOrId)") + .set(httpMethod: "get") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.authConfig+json") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).decode(type: C8yAuthConfig.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Update a specific login option + /// + /// Update a specific login option in the tenant by a given type or ID. + /// + /// + /// > Tip: Required roles + /// ((ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_ADMIN) *AND* tenant access to login option is not restricted by management tenant) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 A login option was updated. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not authorized to perform this operation. + /// * HTTP 404 Login option not found. + /// + /// - Parameters: + /// - body: + /// + /// - typeOrId: + /// The type or ID of the login option. The type's value is case insensitive and can be `OAUTH2`, `OAUTH2_INTERNAL` or `BASIC`. + /// - xCumulocityProcessingMode: + /// Used to explicitly control the processing mode of the request. See [Processing mode](#processing-mode) for more details. + public func updateLoginOption(body: C8yAuthConfig, typeOrId: String, xCumulocityProcessingMode: String? = nil) -> AnyPublisher { + var requestBody = body + requestBody.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/loginOptions/\(typeOrId)") + .set(httpMethod: "put") + .add(header: "X-Cumulocity-Processing-Mode", value: xCumulocityProcessingMode) + .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.authconfig+json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.authconfig+json") + .set(httpBody: encodedRequestBody) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).decode(type: C8yAuthConfig.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Delete a specific login option + /// + /// Delete a specific login option in the tenant by a given type or ID. + /// + /// + /// > Tip: Required roles + /// ((ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_ADMIN) *AND* tenant access to login option is not restricted by management tenant) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 204 A login option was removed. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not authorized to perform this operation. + /// * HTTP 404 Login option not found. + /// + /// - Parameters: + /// - typeOrId: + /// The type or ID of the login option. The type's value is case insensitive and can be `OAUTH2`, `OAUTH2_INTERNAL` or `BASIC`. + public func deleteLoginOption(typeOrId: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/loginOptions/\(typeOrId)") + .set(httpMethod: "delete") + .add(header: "Accept", value: "application/json") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } + /// Update a tenant's access to the login option /// /// Update the tenant's access to the authentication configuration. @@ -126,7 +259,7 @@ public class LoginOptionsApi: AdaptableApi { /// The type or ID of the login option. The type's value is case insensitive and can be `OAUTH2`, `OAUTH2_INTERNAL` or `BASIC`. /// - targetTenant: /// Unique identifier of a Cumulocity IoT tenant. - public func updateLoginOption(body: C8yAuthConfigAccess, typeOrId: String, targetTenant: String) -> AnyPublisher { + public func updateLoginOptionAccess(body: C8yAuthConfigAccess, typeOrId: String, targetTenant: String) -> AnyPublisher { let requestBody = body var encodedRequestBody: Data? = nil do { diff --git a/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift b/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift index 7198d25..e2f9740 100644 --- a/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift @@ -53,7 +53,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - skipChildrenNames: /// When set to `true`, the returned references of child devices won't contain their names. /// - text: - /// Search for managed objects where any property value is equal to the given one. Only string values are supported. + /// Search for managed objects where a property value is equal to the given one.The following properties are examined: `id, type, name, owner, externalIds`. /// - type: /// The type of managed object to search for. /// - withChildren: @@ -68,7 +68,11 @@ public class ManagedObjectsApi: AdaptableApi { /// When set to `true`, the returned result will contain in the statistics object the total number of elements. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). /// - withTotalPages: /// When set to `true`, the returned result will contain in the statistics object the total number of pages. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). - public func getManagedObjects(childAdditionId: String? = nil, childAssetId: String? = nil, childDeviceId: String? = nil, currentPage: Int? = nil, fragmentType: String? = nil, ids: [String]? = nil, onlyRoots: Bool? = nil, owner: String? = nil, pageSize: Int? = nil, q: String? = nil, query: String? = nil, skipChildrenNames: Bool? = nil, text: String? = nil, type: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withGroups: Bool? = nil, withParents: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { + /// - withLatestValues: + /// If set to true the platform returns managed objects with the fragment `c8y_LatestMeasurements, which contains the latest measurement values reported by the device to the platform. + /// + /// **⚠️ Feature Preview:** The parameter is a part of the Latest Measurement feature which is still under public preview. + public func getManagedObjects(childAdditionId: String? = nil, childAssetId: String? = nil, childDeviceId: String? = nil, currentPage: Int? = nil, fragmentType: String? = nil, ids: [String]? = nil, onlyRoots: Bool? = nil, owner: String? = nil, pageSize: Int? = nil, q: String? = nil, query: String? = nil, skipChildrenNames: Bool? = nil, text: String? = nil, type: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withGroups: Bool? = nil, withParents: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil, withLatestValues: Bool? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects") .set(httpMethod: "get") @@ -93,6 +97,7 @@ public class ManagedObjectsApi: AdaptableApi { .add(queryItem: "withParents", value: withParents) .add(queryItem: "withTotalElements", value: withTotalElements) .add(queryItem: "withTotalPages", value: withTotalPages) + .add(queryItem: "withLatestValues", value: withLatestValues) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) @@ -125,7 +130,7 @@ public class ManagedObjectsApi: AdaptableApi { /// > **ⓘ Note** For more details about fragments with specific meanings, review the sections [Device management library](#section/Device-management-library) and [Sensor library](#section/Sensor-library). /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* ROLE_INVENTORY_CREATE + /// ROLE_INVENTORY_ADMIN *OR* ROLE_INVENTORY_CREATE *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* ROLE_MANAGED_OBJECT_CREATE /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -148,6 +153,7 @@ public class ManagedObjectsApi: AdaptableApi { requestBody.childAssets = nil requestBody.creationTime = nil requestBody.childAdditions = nil + requestBody.c8yLatestMeasurements = nil requestBody.`self` = nil requestBody.assetParents = nil requestBody.deviceParents = nil @@ -180,74 +186,13 @@ public class ManagedObjectsApi: AdaptableApi { }).decode(type: C8yManagedObject.self, decoder: JSONDecoder()).eraseToAnyPublisher() } - /// Retrieve the total number of managed objects - /// - /// Retrieve the total number of managed objects (for example, devices, assets, etc.) registered in your tenant, or a subset based on queries. - /// - /// - /// > Tip: Required roles - /// ROLE_INVENTORY_READ is not required, but if the current user doesn't have this role, the response will contain the number of inventory objects accessible for the user. - /// - /// > Tip: Response Codes - /// The following table gives an overview of the possible response codes and their meanings: - /// - /// * HTTP 200 The request has succeeded and the number of managed objects is sent in the response. - /// * HTTP 401 Authentication information is missing or invalid. - /// - /// - Parameters: - /// - childAdditionId: - /// Search for a specific child addition and list all the groups to which it belongs. - /// - childAssetId: - /// Search for a specific child asset and list all the groups to which it belongs. - /// - childDeviceId: - /// Search for a specific child device and list all the groups to which it belongs. - /// - fragmentType: - /// A characteristic which identifies a managed object or event, for example, geolocation, electricity sensor, relay state. - /// - ids: - /// The managed object IDs to search for. - /// - /// **ⓘ Note** If you query for multiple IDs at once, comma-separate the values. - /// - owner: - /// Username of the owner of the managed objects. - /// - text: - /// Search for managed objects where any property value is equal to the given one. Only string values are supported. - /// - type: - /// The type of managed object to search for. - public func getNumberOfManagedObjects(childAdditionId: String? = nil, childAssetId: String? = nil, childDeviceId: String? = nil, fragmentType: String? = nil, ids: [String]? = nil, owner: String? = nil, text: String? = nil, type: String? = nil) -> AnyPublisher { - let builder = URLRequestBuilder() - .set(resourcePath: "/inventory/managedObjects/count") - .set(httpMethod: "get") - .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, text/plain,application/json") - .add(queryItem: "childAdditionId", value: childAdditionId) - .add(queryItem: "childAssetId", value: childAssetId) - .add(queryItem: "childDeviceId", value: childDeviceId) - .add(queryItem: "fragmentType", value: fragmentType) - .add(queryItem: "ids", value: ids, explode: .comma_separated) - .add(queryItem: "owner", value: owner) - .add(queryItem: "text", value: text) - .add(queryItem: "type", value: type) - return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in - guard let httpResponse = element.response as? HTTPURLResponse else { - throw URLError(.badServerResponse) - } - guard (200..<300) ~= httpResponse.statusCode else { - if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { - c8yError.httpResponse = httpResponse - throw c8yError - } - throw BadResponseError(with: httpResponse) - } - return element.data - }).decode(type: Int.self, decoder: JSONDecoder()).eraseToAnyPublisher() - } - /// Retrieve a specific managed object /// /// Retrieve a specific managed object (for example, device, group, template) by a given ID. /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -267,7 +212,11 @@ public class ManagedObjectsApi: AdaptableApi { /// When set to `true`, the returned result will contain the total number of children in the respective objects (`childAdditions`, `childAssets` and `childDevices`). /// - withParents: /// When set to `true`, the returned references of child parents will return the device's parents (if any). Otherwise, it will be an empty array. - public func getManagedObject(id: String, skipChildrenNames: Bool? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withParents: Bool? = nil) -> AnyPublisher { + /// - withLatestValues: + /// If set to true the platform returns managed objects with the fragment `c8y_LatestMeasurements, which contains the latest measurement values reported by the device to the platform. + /// + /// **⚠️ Feature Preview:** The parameter is a part of the Latest Measurement feature which is still under public preview. + public func getManagedObject(id: String, skipChildrenNames: Bool? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withParents: Bool? = nil, withLatestValues: Bool? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)") .set(httpMethod: "get") @@ -276,6 +225,7 @@ public class ManagedObjectsApi: AdaptableApi { .add(queryItem: "withChildren", value: withChildren) .add(queryItem: "withChildrenCount", value: withChildrenCount) .add(queryItem: "withParents", value: withParents) + .add(queryItem: "withLatestValues", value: withLatestValues) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) @@ -301,7 +251,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -326,6 +276,7 @@ public class ManagedObjectsApi: AdaptableApi { requestBody.childAssets = nil requestBody.creationTime = nil requestBody.childAdditions = nil + requestBody.c8yLatestMeasurements = nil requestBody.`self` = nil requestBody.assetParents = nil requestBody.deviceParents = nil @@ -363,9 +314,10 @@ public class ManagedObjectsApi: AdaptableApi { /// Remove a specific managed object (for example, device) by a given ID. /// /// > **ⓘ Note** Inventory DELETE requests are not synchronous. The response could be returned before the delete request has been completed. This may happen especially when the deleted managed object has a lot of associated data. After sending the request, the platform starts deleting the associated data in an asynchronous way. Finally, the requested managed object is deleted after all associated data has been deleted. + /// > **ⓘ Note** By default, the delete operation is always propagated to the subgroups, but only if the deleted object is a group. /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -416,7 +368,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -454,7 +406,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -492,7 +444,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -530,7 +482,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source + /// ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ *OR* owner of the source *OR* MANAGE_OBJECT_READ permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -568,7 +520,7 @@ public class ManagedObjectsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_INVENTORY_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source + /// ROLE_INVENTORY_ADMIN *OR* ROLE_MANAGED_OBJECT_ADMIN *OR* owner of the source *OR* MANAGE_OBJECT_ADMIN permission on the source /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: diff --git a/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift b/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift index b404982..92e234a 100644 --- a/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift @@ -97,7 +97,7 @@ public class MeasurementsApi: AdaptableApi { /// * `value` - The value of the individual measurement. The maximum precision for floating point numbers is 64-bit IEEE 754. For integers it's a 64-bit two's complement integer. The `value` is mandatory for a fragment. /// * `unit` - The unit of the measurements. /// - /// Review the [System of units](#section/System-of-units) section for details about the conversions of units. Also review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) in the Concepts guide. + /// Review the [System of units](#section/System-of-units) section for details about the conversions of units. Also review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation. /// /// The example below uses `c8y_Steam` in the request body to illustrate a fragment for recording temperature measurements. /// @@ -165,7 +165,7 @@ public class MeasurementsApi: AdaptableApi { /// * `value` - The value of the individual measurement. The maximum precision for floating point numbers is 64-bit IEEE 754. For integers it's a 64-bit two's complement integer. The `value` is mandatory for a fragment. /// * `unit` - The unit of the measurements. /// - /// Review the [System of units](#section/System-of-units) section for details about the conversions of units. Also review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) in the Concepts guide. + /// Review the [System of units](#section/System-of-units) section for details about the conversions of units. Also review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation. /// /// The example below uses `c8y_Steam` in the request body to illustrate a fragment for recording temperature measurements. /// @@ -231,7 +231,28 @@ public class MeasurementsApi: AdaptableApi { /// /// DELETE requests are not synchronous. The response could be returned before the delete request has been completed. This may happen especially when there are a lot of measurements to be deleted. /// - /// > **⚠️ Important:** Note that it is possible to call this endpoint without providing any parameter - it may result in deleting all measurements and it is not recommended. + /// > **⚠️ Important:** DELETE requires at least one of the following parameters: `source`, `dateFrom`, `dateTo`. + /// In case of enhanced time series measurements, both `dateFrom` and `dateTo` parameters must be truncated to full hours (for example, 2022-08-19T14:00:00.000Z), otherwise an error will be returned.The `fragmentType` parameter allows to delete measurements only by a measurement fragment when enhanced time series measurements are used.It's not possible to delete by a custom (non-measurement) fragment. + /// + /// Example for a valid measurement value fragment: + /// + /// ``` + /// "c8y_TemperatureMeasurement": { + /// "T": { + /// "value": 28, + /// "unit": "C" + /// } + /// } + /// ``` + /// In the example above `c8y_TemperatureMeasurement` is called fragment and `T` is called series. + /// + /// Example for a non-measurement fragment: + /// + /// ``` + /// "c8y_TemperatureMeasurement": 28 + /// ``` + /// Enhanced Time series measurements will not allow to delete by fragment specific like above. + /// /// /// > Tip: Required roles /// ROLE_MEASUREMENT_ADMIN @@ -242,6 +263,7 @@ public class MeasurementsApi: AdaptableApi { /// * HTTP 204 A collection of measurements was removed. /// * HTTP 401 Authentication information is missing or invalid. /// * HTTP 403 Not authorized to perform this operation. + /// * HTTP 422 Unprocessable Entity – invalid payload. /// /// - Parameters: /// - xCumulocityProcessingMode: @@ -284,7 +306,7 @@ public class MeasurementsApi: AdaptableApi { /// Retrieve a specific measurement /// - /// Retrieve a specific measurement by a given ID. + /// Retrieve a specific measurement by a given ID.Note that you cannot retrieve time series measurements by ID.Instead you can search for such measurements via query parameters.No behavior changes for tenants which do not have time series enabled. /// /// /// > Tip: Required roles @@ -322,7 +344,7 @@ public class MeasurementsApi: AdaptableApi { /// Remove a specific measurement /// - /// Remove a specific measurement by a given ID. + /// Remove a specific measurement by a given ID.Note that you cannot delete time series measurements by ID.Instead, you can delete by query or use the retention rules to remove expired measurements data from the Operational Store.No behavior changes for tenants which do not have time series enabled. /// /// /// > Tip: Required roles @@ -392,7 +414,7 @@ public class MeasurementsApi: AdaptableApi { /// - series: /// The specific series to search for. /// - /// **ⓘ Note** If you query for multiple series at once, comma-separate the values. + /// **ⓘ Note** If you want to query multiple series at once, you must specify the parameter multiple times. /// - source: /// The managed object ID to which the measurement is associated. public func getMeasurementSeries(aggregationType: String? = nil, dateFrom: String, dateTo: String, revert: Bool? = nil, series: [String]? = nil, source: String) -> AnyPublisher { @@ -404,7 +426,7 @@ public class MeasurementsApi: AdaptableApi { .add(queryItem: "dateFrom", value: dateFrom) .add(queryItem: "dateTo", value: dateTo) .add(queryItem: "revert", value: revert) - .add(queryItem: "series", value: series, explode: .comma_separated) + .add(queryItem: "series", value: series, explode: .exploded) .add(queryItem: "source", value: source) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { diff --git a/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift b/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift index 8e906bf..6edbf1b 100644 --- a/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift @@ -83,6 +83,10 @@ public class NewDeviceRequestsApi: AdaptableApi { /// Used to explicitly control the processing mode of the request. See [Processing mode](#processing-mode) for more details. public func createNewDeviceRequest(body: C8yNewDeviceRequest, xCumulocityProcessingMode: String? = nil) -> AnyPublisher { var requestBody = body + requestBody.owner = nil + requestBody.securityToken = nil + requestBody.creationTime = nil + requestBody.tenantId = nil requestBody.`self` = nil requestBody.status = nil var encodedRequestBody: Data? = nil @@ -173,8 +177,13 @@ public class NewDeviceRequestsApi: AdaptableApi { /// Unique identifier of the new device request. public func updateNewDeviceRequest(body: C8yNewDeviceRequest, requestId: String) -> AnyPublisher { var requestBody = body + requestBody.owner = nil + requestBody.creationTime = nil + requestBody.groupId = nil + requestBody.tenantId = nil requestBody.`self` = nil requestBody.id = nil + requestBody.type = nil var encodedRequestBody: Data? = nil do { encodedRequestBody = try JSONEncoder().encode(requestBody) diff --git a/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift b/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift index 17917f7..017a8ab 100644 --- a/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift @@ -97,6 +97,10 @@ public class OperationsApi: AdaptableApi { /// /// It is possible to add custom fragments to operations, for example `com_cumulocity_model_WebCamDevice` is a custom object of the webcam operation. /// + /// There are some custom fragments which are used by web applications (like Device management) to provide additional context to operations, for example: + /// + /// * `description` - this fragment can be used to provide a brief user-friendly description of the operation which is later displayed in the operations list views within web applications. + /// /// /// > Tip: Required roles /// ROLE_DEVICE_CONTROL_ADMIN *OR* owner of the device *OR* ADMIN permissions on the device @@ -272,7 +276,6 @@ public class OperationsApi: AdaptableApi { requestBody.creationTime = nil requestBody.deviceExternalIDs?.`self` = nil requestBody.bulkOperationId = nil - requestBody.failureReason = nil requestBody.`self` = nil requestBody.id = nil requestBody.deviceId = nil diff --git a/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift index f86e531..684c25c 100644 --- a/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift @@ -69,7 +69,7 @@ public class OptionsApi: AdaptableApi { /// > Tip: Default option categories /// **access.control** /// - /// | Key | Default value | Predefined | Description ||--|--|--|--|| allow.origin | * | Yes | Comma separated list of domains allowed for execution of CORS. Wildcards are allowed (for example, `*.cumuclocity.com`) | + /// | Key | Default value | Predefined | Description ||--|--|--|--|| allow.origin | * | Yes | Comma separated list of domains allowed for execution of CORS. Wildcards are allowed (for example, `*.cumulocity.com`) | /// /// **alarm.type.mapping** /// diff --git a/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift b/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift index 79e7efc..a22bffb 100644 --- a/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift @@ -183,6 +183,8 @@ import Combine /// } /// ] /// ``` +/// > Tip: Long-running connections +/// To keep a long-running connection alive when there are no new notifications to deliver, the server will periodically send an empty `/meta/connect` response to the client.The client should send a new `/meta/connect` request immediately after receiving such a response, to ensure that the connection remains active and future notifications are delivered. public class RealtimeNotificationApi: AdaptableApi { /// Responsive communication diff --git a/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift index dc8d1d1..584dcc6 100644 --- a/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift @@ -36,9 +36,17 @@ public class SubscriptionsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - source: /// The managed object ID to which the subscription is associated. + /// - subscription: + /// The subscription name by which filtering will be done. + /// - typeFilter: + /// The type used to filter subscriptions. This will check the subscription's `subscriptionFilter.typeFilter` field. + /// + /// **ⓘ Note** Filtering by `typeFilter` may affect paging. Additional post filtering may be performed if OData-like expressions are used in the subscriptions. + /// - withTotalElements: + /// When set to `true`, the returned result will contain in the statistics object the total number of elements. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). /// - withTotalPages: /// When set to `true`, the returned result will contain in the statistics object the total number of pages. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). - public func getSubscriptions(context: String? = nil, currentPage: Int? = nil, pageSize: Int? = nil, source: String? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { + public func getSubscriptions(context: String? = nil, currentPage: Int? = nil, pageSize: Int? = nil, source: String? = nil, subscription: String? = nil, typeFilter: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/notification2/subscriptions") .set(httpMethod: "get") @@ -47,6 +55,9 @@ public class SubscriptionsApi: AdaptableApi { .add(queryItem: "currentPage", value: currentPage) .add(queryItem: "pageSize", value: pageSize) .add(queryItem: "source", value: source) + .add(queryItem: "subscription", value: subscription) + .add(queryItem: "typeFilter", value: typeFilter) + .add(queryItem: "withTotalElements", value: withTotalElements) .add(queryItem: "withTotalPages", value: withTotalPages) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { @@ -74,6 +85,7 @@ public class SubscriptionsApi: AdaptableApi { /// * The name of the subscription. /// * The applicable filter criteria. /// * The option to only include specific custom fragments in the forwarded data. + /// * The option to use persistent or non-persistent message storage. /// /// /// > Tip: Required roles @@ -150,7 +162,7 @@ public class SubscriptionsApi: AdaptableApi { /// - context: /// The context to which the subscription is associated. /// - /// **ⓘ Note** If the value is `mo`, then `source` must also be provided in the query. + /// **ⓘ Note** If the value is `mo` (managed object), then `source` must also be provided in the query. /// - source: /// The managed object ID to which the subscription is associated. public func deleteSubscriptions(xCumulocityProcessingMode: String? = nil, context: String? = nil, source: String? = nil) -> AnyPublisher { diff --git a/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift b/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift index 0097392..6078a36 100644 --- a/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift @@ -21,7 +21,7 @@ import Combine /// > **⚠️ Important:** For support user access, the tenant ID must be used and not the tenant domain. /// See [Tenant > Current tenant](#operation/getCurrentTenantResource) for information on how to retrieve the tenant ID and domain of the current tenant via the API. /// -/// In the UI, the tenant ID is displayed in the user dropdown menu, see [Getting started > User options and settings](https://cumulocity.com/guides/users-guide/getting-started/#user-settings) in the User guide. +/// In the UI, the tenant ID is displayed in the user dropdown menu, see [Getting started > Get familiar with the UI > User options and settings](https://cumulocity.com/docs/get-familiar-with-the-ui/user-settings/) in the Cumulocity IoT user documentation. /// /// > Tip: Access rights and permissions /// There are two types of roles in Cumulocity IoT – global and inventory. Global roles are applied at the tenant level. In a Role Based Access Control (RBAC) approach you must use the inventory roles in order to have the correct level of separation. Apart from some global permissions (like "own user management") customer users will not be assigned any roles. Inventory roles must be created, or the default roles used, and then assigned to the user in combination with the assets the roles apply to. This needs to be done at least once for each customer. @@ -30,7 +30,7 @@ import Combine /// /// In the RBAC approach, managing access is the most complicated part because a misconfiguration can potentially give customers access to data that they must not see, like other customers' data. The inventory roles allow you to granularly define access for only certain parts of data, but they don't protect you from accidental misconfigurations. A limitation here is that customers won't be able to create their own roles. /// -/// For more details, see [RBAC versus multi-tenancy approach](https://cumulocity.com/guides/concepts/tenant-hierarchy/#comparison). +/// For more details, see [RBAC versus multi-tenancy approach](https://cumulocity.com/docs/concepts/tenant-hierarchy/#comparison-of-various-use-cases). /// /// > **ⓘ Note** The Accept header should be provided in all POST/PUT requests, otherwise an empty response body will be returned. public class TenantsApi: AdaptableApi { @@ -58,7 +58,13 @@ public class TenantsApi: AdaptableApi { /// When set to `true`, the returned result will contain in the statistics object the total number of elements. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). /// - withTotalPages: /// When set to `true`, the returned result will contain in the statistics object the total number of pages. Only applicable on [range queries](https://en.wikipedia.org/wiki/Range_query_(database)). - public func getTenants(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { + /// - company: + /// Company name associated with the Cumulocity IoT tenant. + /// - domain: + /// Domain name of the Cumulocity IoT tenant. + /// - parent: + /// Identifier of the Cumulocity IoT tenant's parent. + public func getTenants(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil, company: String? = nil, domain: String? = nil, parent: String? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants") .set(httpMethod: "get") @@ -67,6 +73,9 @@ public class TenantsApi: AdaptableApi { .add(queryItem: "pageSize", value: pageSize) .add(queryItem: "withTotalElements", value: withTotalElements) .add(queryItem: "withTotalPages", value: withTotalPages) + .add(queryItem: "company", value: company) + .add(queryItem: "domain", value: domain) + .add(queryItem: "parent", value: parent) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) @@ -183,7 +192,7 @@ public class TenantsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_TENANT_MANAGEMENT_READ *AND* the current tenant is its parent *OR* is the management tenant + /// ROLE_TENANT_MANAGEMENT_READ *AND* (the current tenant is its parent *OR* is the management tenant) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -222,7 +231,8 @@ public class TenantsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_UPDATE) *AND* (the current tenant is its parent *AND* the current tenant is allowed to create subtenants) *OR* is the management tenant + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_UPDATE) *AND* + /// ((the current tenant is its parent *AND* the current tenant is allowed to create subtenants) *OR* is the management tenant) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -354,4 +364,53 @@ public class TenantsApi: AdaptableApi { return element.data }).decode(type: C8yTenantTfaData.self, decoder: JSONDecoder()).eraseToAnyPublisher() } + + /// Sets TFA settings for a specific tenant + /// + /// Sets the two-factor authentication settings of a specific tenant for a specific tenant ID. + /// + /// + /// > Tip: Required roles + /// ((ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_UPDATE) *AND* (the current tenant is its parent *OR* the current user belongs to the tenant))) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 204 The tenant's TFA configuration was updated. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 404 Tenant not found. + /// + /// - Parameters: + /// - body: + /// + /// - tenantId: + /// Unique identifier of a Cumulocity IoT tenant. + public func updateTenantTfaSettings(body: C8yTenantTfaStrategy, tenantId: String) -> AnyPublisher { + let requestBody = body + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/tenants/\(tenantId)/tfa") + .set(httpMethod: "put") + .add(header: "Content-Type", value: "application/json") + .add(header: "Accept", value: "application/json") + .set(httpBody: encodedRequestBody) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } } diff --git a/Sources/CumulocityCoreLibrary/Api/TokensApi.swift b/Sources/CumulocityCoreLibrary/Api/TokensApi.swift index 1592802..62daaac 100644 --- a/Sources/CumulocityCoreLibrary/Api/TokensApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TokensApi.swift @@ -21,6 +21,10 @@ public class TokensApi: AdaptableApi { /// * The subscriber name which the client wishes to be identified with. /// * The subscription name. This value must be associated with a subscription that's already been created and in essence, the obtained token will give the ability to read notifications for the subscription that is specified here. /// * The token expiration duration. + /// * The option to disable signing of the token by the Cumulocity IoT platform. + /// * The subscription type that the token should be associated with. + /// * The option to use the token to create shared consumers of the subscription. + /// * The option to select the non-persistent variant of the subscription, if one exists. /// /// /// > Tip: Required roles diff --git a/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift b/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift index 035d788..ed0723c 100644 --- a/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift @@ -11,7 +11,7 @@ import Combine /// API methods for managing trusted certificates used to establish device connections via MQTT. /// -/// More detailed information about trusted certificates and their role can be found in [Device management > Managing device data](https://cumulocity.com/guides/users-guide/device-management/#managing-device-data) in the *User guide*. +/// More detailed information about trusted certificates and their role can be found in [Device management > Device management application > Managing device data](https://cumulocity.com/docs/device-management-application/managing-device-data/) in the Cumulocity IoT user documentation. /// /// > **ⓘ Note** The Accept header must be provided in all POST/PUT requests, otherwise an empty response body will be returned. public class TrustedCertificatesApi: AdaptableApi { @@ -22,7 +22,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// /// /// > Tip: Required roles - /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN) *AND* (is the current tenant) + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_READ) *AND* (is the current tenant) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -89,17 +89,14 @@ public class TrustedCertificatesApi: AdaptableApi { /// /// - tenantId: /// Unique identifier of a Cumulocity IoT tenant. - public func addTrustedCertificate(body: C8yTrustedCertificate, tenantId: String) -> AnyPublisher { - var requestBody = body - requestBody.notAfter = nil - requestBody.serialNumber = nil - requestBody.subject = nil - requestBody.fingerprint = nil - requestBody.`self` = nil - requestBody.algorithmName = nil - requestBody.version = nil - requestBody.issuer = nil - requestBody.notBefore = nil + /// - xCumulocityProcessingMode: + /// Used to explicitly control the processing mode of the request. See [Processing mode](#processing-mode) for more details. + /// - addToTrustStore: + /// If set to `true` the certificate is added to the truststore. + /// + /// The truststore contains all trusted certificates. A connection to a device is only established if it connects to Cumulocity IoT with a certificate in the truststore. + public func addTrustedCertificate(body: C8yUploadedTrustedCertificate, tenantId: String, xCumulocityProcessingMode: String? = nil, addToTrustStore: Bool? = nil) -> AnyPublisher { + let requestBody = body var encodedRequestBody: Data? = nil do { encodedRequestBody = try JSONEncoder().encode(requestBody) @@ -109,8 +106,10 @@ public class TrustedCertificatesApi: AdaptableApi { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates") .set(httpMethod: "post") + .add(header: "X-Cumulocity-Processing-Mode", value: xCumulocityProcessingMode) .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") + .add(queryItem: "addToTrustStore", value: addToTrustStore) .set(httpBody: encodedRequestBody) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { @@ -149,7 +148,11 @@ public class TrustedCertificatesApi: AdaptableApi { /// /// - tenantId: /// Unique identifier of a Cumulocity IoT tenant. - public func addTrustedCertificates(body: C8yTrustedCertificateCollection, tenantId: String) -> AnyPublisher { + /// - addToTrustStore: + /// If set to `true` the certificate is added to the truststore. + /// + /// The truststore contains all trusted certificates. A connection to a device is only established if it connects to Cumulocity IoT with a certificate in the truststore. + public func addTrustedCertificates(body: C8yUploadedTrustedCertificateCollection, tenantId: String, addToTrustStore: Bool? = nil) -> AnyPublisher { var requestBody = body requestBody.next = nil requestBody.prev = nil @@ -166,6 +169,7 @@ public class TrustedCertificatesApi: AdaptableApi { .set(httpMethod: "post") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") + .add(queryItem: "addToTrustStore", value: addToTrustStore) .set(httpBody: encodedRequestBody) return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in guard let httpResponse = element.response as? HTTPURLResponse else { @@ -188,7 +192,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// /// /// > Tip: Required roles - /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN) *AND* (is the current tenant *OR* is the management tenant) + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_READ) *AND* (is the current tenant *OR* is the management tenant) /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -246,16 +250,19 @@ public class TrustedCertificatesApi: AdaptableApi { /// Unique identifier of a trusted certificate. public func updateTrustedCertificate(body: C8yTrustedCertificate, tenantId: String, fingerprint: String) -> AnyPublisher { var requestBody = body + requestBody.proofOfPossessionValid = nil requestBody.notAfter = nil requestBody.serialNumber = nil + requestBody.proofOfPossessionVerificationCodeUsableUntil = nil requestBody.subject = nil - requestBody.fingerprint = nil - requestBody.`self` = nil - requestBody.certInPemFormat = nil requestBody.algorithmName = nil requestBody.version = nil requestBody.issuer = nil requestBody.notBefore = nil + requestBody.proofOfPossessionUnsignedVerificationCode = nil + requestBody.fingerprint = nil + requestBody.`self` = nil + requestBody.certInPemFormat = nil var encodedRequestBody: Data? = nil do { encodedRequestBody = try JSONEncoder().encode(requestBody) @@ -285,7 +292,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// Remove a stored certificate /// - /// Remove a stored trusted certificate (by a given fingerprint) from a specific tenant (by a given ID). + /// Remove a stored trusted certificate (by a given fingerprint) from a specific tenant (by a given ID).When a trusted certificate is deleted, the established MQTT connection to all devices that are using the corresponding certificate are closed. /// /// /// > Tip: Required roles @@ -456,4 +463,297 @@ public class TrustedCertificatesApi: AdaptableApi { return element.data }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() } + + /// Verify a certificate chain + /// + /// Verify a device certificate chain against a specific tenant using file upload or by HTTP headers.The tenant ID is `optional` and this api will try to resolve the tenant from the chain if not found in the request header.For file upload, the max chain length support is 10 and for a header it is 5. + /// + /// If CRL (certificate revocation list) check is enabled on the tenant and the certificate chain is identified to be revoked during validation the further validation of the chain stops and returns unauthorized. + /// + /// > **ⓘ Note** File upload takes precedence over HTTP headers if both are passed. + /// + /// > Tip: Required roles + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_READ) *AND* (is the current tenant *OR* is current management tenant) *OR* (is authenticated *AND* is current user service user) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The certificate chain is valid and not revoked. + /// * HTTP 400 Unable to parse certificate chain. + /// * HTTP 401 One or more certificates in the chain are revoked or the certificate chain is not valid. Revoked certificates are checked first, then the validity of the certificate chain. + /// * HTTP 403 Not enough permissions/roles to perform this operation. + /// * HTTP 404 The tenant ID does not exist. + /// + /// - Parameters: + /// - tenantId: + /// + /// - file: + /// File to be uploaded. + /// - xCumulocityClientCertChain: + /// Used to send a certificate chain in the header. Separate the chain with `,` and also each 64 bit block with ` ` (a space character). + public func validateChain(tenantId: String, file: Data, xCumulocityClientCertChain: String) -> AnyPublisher { + let multipartBuilder = MultipartFormDataBuilder() + do { + try multipartBuilder.addBodyPart(named: "tenantId", codable: tenantId, mimeType: "text/plain"); + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + do { + try multipartBuilder.addBodyPart(named: "file", codable: file, mimeType: "text/plain"); + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/trusted-certificates/verify-cert-chain") + .set(httpMethod: "post") + .add(header: "X-Cumulocity-Client-Cert-Chain", value: xCumulocityClientCertChain) + .add(header: "Content-Type", value: "multipart/form-data") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") + .add(header: "Content-Type", value: multipartBuilder.contentType) + .set(httpBody: multipartBuilder.build()) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).decode(type: C8yVerifyCertificateChain.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Get revoked certificates + /// + /// This endpoint downloads current CRL file containing list of revoked certificate ina binary file format with `content-type` as `application/pkix-crl`. + /// + /// + /// > Tip: Required roles + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_READ) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The CRL file of the current tenant. + public func downloadCrl() -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/trusted-certificates/settings/crl") + .set(httpMethod: "get") + .add(header: "Accept", value: "application/pkix-crl") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } + + /// Add revoked certificates + /// + /// > **ⓘ Note** A certificate revocation list (CRL) is a list of digital certificatesthat have been revoked by the issuing certificate authority (CA) before expiration date.In Cumulocity IoT, a CRL check can be in online or offline mode or both. + /// An endpoint to add revoked certificate serial numbers for offline CRL check via payload or file. + /// + /// For payload, a JSON object required with list of CRL entries, for example: + /// + /// ```json + /// { + /// "crls": [ + /// { + /// "serialNumberInHex": "1000", + /// "revocationDate": "2023-01-11T16:12:36.288Z" + /// } + /// ] + /// } + /// ``` + /// Each entry is composed of: + /// + /// * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g As (1000)^16 == (4096)^10, So we have to enter 1000.If duplicate serial number exists in payload, the existing entry stays
+ /// * `revocationDate` - accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`, for example: `2023-01-11T16:12:36.288Z`.This is an optional parameter and defaults to the current server UTC date time if not specified in the payload.If specified and the date is in future then those entries will be also defaulted to current date. + /// + /// For file upload, each file can hold at maximum 5000 revocation entries.Multiple upload is allowed.In case of duplicates, the latest (last uploaded) entry is considered. + /// + /// See below for a sample CSV file: + /// + /// | SERIAL NO. | REVOCATION DATE ||--|--|| 1000 | 2023-01-11T16:12:36.288Z | + /// + /// Each entry is composed of : + /// + /// * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g (1000)^16 == (4096)^10, So we have to enter 1000.If duplicate serial number exists in payload, the latest entry will be taken.
+ /// * revocationDate: Accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` e.g: 2023-01-11T16:12:36.288Z.This is an optional and will be default to current server UTC date time if not specified in payload.If specified and the date is in future then those entries will be skipped. + /// + /// The CRL setting for offline and online check can be enabled/disabled using /tenant/options.Keys are `crl.online.check.enabled` and `crl.offline.check.enabled` under the category `configuration`. + /// + /// + /// > Tip: Required roles + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN) *AND* is the current tenant + /// + /// **⚠️ Important:** According to CRL policy, added serial numbers cannot be reversed. + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 204 CRLs updated successfully. + /// * HTTP 400 Unsupported date time format. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not enough permissions/roles to perform this operation. + /// + /// - Parameters: + /// - body: + /// + public func updateCRL(body: C8yUpdateCRLEntries) -> AnyPublisher { + let requestBody = body + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/trusted-certificates/settings/crl") + .set(httpMethod: "put") + .add(header: "Content-Type", value: "application/json") + .add(header: "Accept", value: "application/json") + .set(httpBody: encodedRequestBody) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } + + /// Add revoked certificates + /// + /// > **ⓘ Note** A certificate revocation list (CRL) is a list of digital certificatesthat have been revoked by the issuing certificate authority (CA) before expiration date.In Cumulocity IoT, a CRL check can be in online or offline mode or both. + /// An endpoint to add revoked certificate serial numbers for offline CRL check via payload or file. + /// + /// For payload, a JSON object required with list of CRL entries, for example: + /// + /// ```json + /// { + /// "crls": [ + /// { + /// "serialNumberInHex": "1000", + /// "revocationDate": "2023-01-11T16:12:36.288Z" + /// } + /// ] + /// } + /// ``` + /// Each entry is composed of: + /// + /// * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g As (1000)^16 == (4096)^10, So we have to enter 1000.If duplicate serial number exists in payload, the existing entry stays
+ /// * `revocationDate` - accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`, for example: `2023-01-11T16:12:36.288Z`.This is an optional parameter and defaults to the current server UTC date time if not specified in the payload.If specified and the date is in future then those entries will be also defaulted to current date. + /// + /// For file upload, each file can hold at maximum 5000 revocation entries.Multiple upload is allowed.In case of duplicates, the latest (last uploaded) entry is considered. + /// + /// See below for a sample CSV file: + /// + /// | SERIAL NO. | REVOCATION DATE ||--|--|| 1000 | 2023-01-11T16:12:36.288Z | + /// + /// Each entry is composed of : + /// + /// * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g (1000)^16 == (4096)^10, So we have to enter 1000.If duplicate serial number exists in payload, the latest entry will be taken.
+ /// * revocationDate: Accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` e.g: 2023-01-11T16:12:36.288Z.This is an optional and will be default to current server UTC date time if not specified in payload.If specified and the date is in future then those entries will be skipped. + /// + /// The CRL setting for offline and online check can be enabled/disabled using /tenant/options.Keys are `crl.online.check.enabled` and `crl.offline.check.enabled` under the category `configuration`. + /// + /// + /// > Tip: Required roles + /// (ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_ADMIN) *AND* is the current tenant + /// + /// **⚠️ Important:** According to CRL policy, added serial numbers cannot be reversed. + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 204 CRLs updated successfully. + /// * HTTP 400 Unsupported date time format. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not enough permissions/roles to perform this operation. + /// + /// - Parameters: + /// - file: + /// File to be uploaded. + public func updateCRL(file: Data) -> AnyPublisher { + let multipartBuilder = MultipartFormDataBuilder() + do { + try multipartBuilder.addBodyPart(named: "file", codable: file, mimeType: "text/plain"); + } catch { + return Fail(error: error).eraseToAnyPublisher() + } + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/trusted-certificates/settings/crl") + .set(httpMethod: "put") + .add(header: "Content-Type", value: "multipart/form-data") + .add(header: "Accept", value: "application/json") + .add(header: "Content-Type", value: multipartBuilder.contentType) + .set(httpBody: multipartBuilder.build()) + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } + + /// Obtain device access token + /// + /// Only those devices which are registered to use cert auth can authenticate via mTLS protocol and retrieve JWT token. Device access token API works only on port 8443 via mutual TLS (mTLS) connection.Immediate issuer of client certificate must present in Platform's truststore, if not then whole certificate chain needs to send in header and root or any intermediate certificate must be present in the Platform's truststore.We must have the following: + /// + /// * private_key + /// * client certificate + /// * whole certificate chain (Optional - This API requires the client to send a custom header `X-SSL-CERT-CHAIN` only if the immediate issuer of the client's certificate is not uploaded as a trusted certificate on the platform. If the immediate issuer is already uploaded and trusted, the header can be omitted) + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 Successfully retrieved device access token from device certificate. + /// * HTTP 400 Unable to parse certificate chain. + /// * HTTP 401 One or more certificates in the chain are revoked or the certificate chain is not valid. Revoked certificates are checked first, then the validity of the certificate chain. + /// * HTTP 404 Device access token feature is disabled. + /// * HTTP 422 The verification was not successful. + /// + /// - Parameters: + /// - xSslCertChain: + /// Used to send a certificate chain in the header. Separate the chain with ` ` (a space character) and also each 64 bit block with ` ` (a space character). + public func obtainAccessToken(xSslCertChain: String? = nil) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/devicecontrol/deviceAccessToken") + .set(httpMethod: "post") + .add(header: "X-Ssl-Cert-Chain", value: xSslCertChain) + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).decode(type: C8yAccessToken.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } } diff --git a/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift b/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift index 060b9cc..891e76c 100644 --- a/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift @@ -47,6 +47,38 @@ import Combine /// The microservice usage statistics gathers information on the resource usage for tenants for each subscribed application which are collected on a daily base. /// /// The microservice usage's information is stored in the `resources` object. +/// +/// > Tip: Frequently asked questions +/// > Tip: Which requests are counted as general "requestCount"? +/// All requests which the platform receives are counted, including,for example, UI requests, microservices requests, device requests and agents requests. Only a few internal endpoints are not counted: +/// +/// * `/health` (and all endpoints including this URI fragment, like `/tenant/health`) +/// * `/application/currentApplication` (and all subresources, like `/application/currentApplication/subscriptions`) +/// * `/tenant/limit` +/// * `/devicecontrol/deviceCredentials` +/// * `/inventory/templates` (and all subresources) +/// +/// > Tip: My devices are not sending any data, but "requestCount" is increasing, and the total number is really big. Why is this happening? +/// Not only device requests are counted. Every user interaction with UI applications generates some requests to the backend API. Additionally you may have subscribed standard or custom microservices, which also regularly send requests to the platform. +/// +/// Example: If you have four microservices and each microservice sends five requests per minute, this setup creates `4 * 5 * 60 * 24 = 28800` requests per day. Similar numbers arise if there are multiple users working with the given tenant UI concurrently. +/// +/// > Tip: Which requests are counted as "deviceRequestCount"? +/// All requests from "requestCount" except the following: +/// +/// * Tenant API requests +/// * Application API requests +/// * User API requests +/// * Requests with the proper HTTP header `X-Cumulocity-Application-Key`, matching the application key of one of the applications used by a particular tenant +/// +/// The exclusion of the APIs in the list above means that requests to endpoints which start with the mentioned API prefixes are not counted. For example, for the Tenant API the following endpoints are not counted (the list is incomplete): +/// +/// * `/tenant/tenants` +/// * `/tenant/currentTenant` +/// * `/tenant/statistics` +/// * `/tenant/options` +/// +/// > **ⓘ Note** Each microservice and web application must include the `X-Cumulocity-Application-Key` header in all requests.Otherwise such requests are counted as device requests which incorrectly affects the "deviceRequestCount" usage metric. public class UsageStatisticsApi: AdaptableApi { /// Retrieve statistics of the current tenant @@ -108,8 +140,7 @@ public class UsageStatisticsApi: AdaptableApi { /// /// /// > Tip: Required roles - /// ROLE_TENANT_STATISTICS_READ *OR* ROLE_INVENTORY_READ - /// If the `tenant` request parameter is specified, then the current tenant must be the management tenant *OR* the parent of the requested `tenant`. + /// ROLE_TENANT_STATISTICS_READ *OR* ROLE_INVENTORY_READ *OR* ROLE_MANAGED_OBJECT_READ If the `tenant` request parameter is specified, then the current tenant must be the management tenant *OR* the parent of the requested `tenant`. /// /// > Tip: Response Codes /// The following table gives an overview of the possible response codes and their meanings: @@ -194,6 +225,9 @@ public class UsageStatisticsApi: AdaptableApi { /// /// Retrieve usage statistics summary files report metadata. /// + /// > **ⓘ Note** This is only accessible by the Management tenant. + /// Date range defines the search criteria for files which have any data inside this range. For example, query containing `dateFrom=2023-03-01&dateTo=2023-03-31`will return files with statistics from ranges 2023-02-25---2023-03-03, 2023-03-04---2023-03-05, 2023-03-15---2023-04-15, but not the files wherethe whole range of data is outside of queried range, like 2023-02-01---2023-02-27. + /// /// /// > Tip: Required roles /// ROLE_TENANT_MANAGEMENT_ADMIN @@ -208,9 +242,9 @@ public class UsageStatisticsApi: AdaptableApi { /// - currentPage: /// The current page of the paginated results. /// - dateFrom: - /// Start date or date and time of the statistics file generation. + /// Start date or date and time of the range included in statistics file. /// - dateTo: - /// End date or date and time of the statistics file generation. + /// End date or date and time of the range included in statistics file. /// - pageSize: /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - withTotalPages: @@ -249,6 +283,9 @@ public class UsageStatisticsApi: AdaptableApi { /// * REAL - generated by the system on the first day of the month and including statistics from the previous month. /// * TEST - generated by the user with a time range specified in the query parameters (`dateFrom`, `dateTo`). /// + /// `dateFrom` and `dateTo` are using daily granularity and each day is stored with respect to local Time Zone of the server. + /// + /// > **ⓘ Note** This is only accessible by the Management tenant. /// /// > Tip: Required roles /// ROLE_TENANT_MANAGEMENT_ADMIN *OR* ROLE_TENANT_MANAGEMENT_CREATE @@ -296,6 +333,7 @@ public class UsageStatisticsApi: AdaptableApi { /// /// Retrieve a specific usage statistics file (by a given ID). /// + /// > **ⓘ Note** This is only accessible by the Management tenant. /// /// > Tip: Required roles /// ROLE_TENANT_MANAGEMENT_ADMIN @@ -339,6 +377,7 @@ public class UsageStatisticsApi: AdaptableApi { /// * REAL - generated by the system on the first day of the month and includes statistics for the previous month. /// * TEST - generated by the user with a time range specified in the query parameters (`dateFrom`, `dateTo`). /// + /// > **ⓘ Note** This is only accessible by the Management tenant. /// /// > Tip: Required roles /// ROLE_TENANT_MANAGEMENT_ADMIN diff --git a/Sources/CumulocityCoreLibrary/Api/UsersApi.swift b/Sources/CumulocityCoreLibrary/Api/UsersApi.swift index dca79d0..9113770 100644 --- a/Sources/CumulocityCoreLibrary/Api/UsersApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/UsersApi.swift @@ -194,6 +194,8 @@ public class UsersApi: AdaptableApi { /// /// When the user is updated with changed permissions or groups, a corresponding audit record is created with type "User" and activity "User updated". /// + /// Note that you cannot update the password or email of another user, they can only be updated for the current user. + /// /// /// > Tip: Required roles /// ROLE_USER_MANAGEMENT_ADMIN to update root users in a user hierarchy *OR* users that are not in any hierarchy @@ -298,63 +300,6 @@ public class UsersApi: AdaptableApi { }).eraseToAnyPublisher() } - /// Update a specific user's password of a specific tenant - /// - /// Update a specific user's password (by a given user ID) of a specific tenant (by a given tenant ID). - /// - /// Changing the user's password creates a corresponding audit record of type "User" and activity "User updated", and specifying that the password has been changed. - /// - /// > **⚠️ Important:** If the tenant uses OAI-Secure authentication, the target user will be logged out. - /// - /// > Tip: Required roles - /// ROLE_USER_MANAGEMENT_ADMIN to update root users in a user hierarchy *OR* users that are not in any hierarchy - /// ROLE_USER_MANAGEMENT_ADMIN to update non-root users in a user hierarchy *AND* whose parents have access to assigned roles, groups, device permissions and applications - /// ROLE_USER_MANAGEMENT_CREATE to update descendants of the current user in a user hierarchy *AND* whose parents have access to assigned roles, groups, device permissions and applications - /// - /// > Tip: Response Codes - /// The following table gives an overview of the possible response codes and their meanings: - /// - /// * HTTP 200 A user was updated. - /// * HTTP 401 Authentication information is missing or invalid. - /// * HTTP 403 Not enough permissions/roles to perform this operation. - /// * HTTP 422 Unprocessable Entity – invalid payload. - /// - /// - Parameters: - /// - body: - /// - /// - tenantId: - /// Unique identifier of a Cumulocity IoT tenant. - /// - userId: - /// Unique identifier of the a user. - public func updateUserPassword(body: C8yPasswordChange, tenantId: String, userId: String) -> AnyPublisher { - let requestBody = body - var encodedRequestBody: Data? = nil - do { - encodedRequestBody = try JSONEncoder().encode(requestBody) - } catch { - return Fail(error: error).eraseToAnyPublisher() - } - let builder = URLRequestBuilder() - .set(resourcePath: "/user/\(tenantId)/users/\(userId)/password") - .set(httpMethod: "put") - .add(header: "Content-Type", value: "application/json") - .add(header: "Accept", value: "application/json") - .set(httpBody: encodedRequestBody) - return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in - guard let httpResponse = element.response as? HTTPURLResponse else { - throw URLError(.badServerResponse) - } - guard (200..<300) ~= httpResponse.statusCode else { - if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { - c8yError.httpResponse = httpResponse - throw c8yError - } - throw BadResponseError(with: httpResponse) - } - return element.data - }).eraseToAnyPublisher() - } - /// Retrieve the TFA settings of a specific user /// /// Retrieve the two-factor authentication settings for the specified user. @@ -623,4 +568,44 @@ public class UsersApi: AdaptableApi { return element.data }).eraseToAnyPublisher() } + + /// Terminate all tenant users' sessions and invalidate tokens + /// + /// The user with the role ROLE_USER_MANAGEMENT_ADMIN is authorized to log out all tenant users with a toked based access. + /// + /// The request is responsible for terminating all tenant users' toked based sessions and invalidating internal platform access tokens. + /// + /// + /// > Tip: Required roles + /// ROLE_USER_MANAGEMENT_ADMIN *AND* is the current tenant + /// + /// > Tip: Response Codes + /// The following table gives an overview of the possible response codes and their meanings: + /// + /// * HTTP 200 The request has succeeded and the users (with a token based access) are logged out. + /// * HTTP 401 Authentication information is missing or invalid. + /// * HTTP 403 Not enough permissions/roles to perform this operation. + /// + /// - Parameters: + /// - tenantId: + /// Unique identifier of a Cumulocity IoT tenant. + public func logoutAllUsers(tenantId: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/user/logout/\(tenantId)/allUsers") + .set(httpMethod: "post") + .add(header: "Accept", value: "application/json") + return self.session.dataTaskPublisher(for: adapt(builder: builder).build()).tryMap({ element -> Data in + guard let httpResponse = element.response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + c8yError.httpResponse = httpResponse + throw c8yError + } + throw BadResponseError(with: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift b/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift index 66810a0..722001d 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift @@ -9,7 +9,7 @@ import Foundation public struct C8yAlarm: Codable { - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.count = try container.decodeIfPresent(Int.self, forKey: .count) @@ -91,7 +91,7 @@ public struct C8yAlarm: Codable { /// It is possible to add an arbitrary number of additional properties as a list of key-value pairs, for example, `"property1": {}`, `"property2": "value"`. These properties are known as custom fragments and can be of any type, for example, object or string. Each custom fragment is identified by a unique name. /// - /// Review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) as there are characters that can not be used when naming custom fragments. + /// Review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation as there are characters that can not be used when naming custom fragments. public var customFragments: [String: Any] = [:] public subscript(key: String) -> Any? { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yApplication.swift b/Sources/CumulocityCoreLibrary/Model/C8yApplication.swift index 2d59cd6..80c50e8 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yApplication.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yApplication.swift @@ -9,7 +9,7 @@ import Foundation public struct C8yApplication: Codable { - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.availability = try container.decodeIfPresent(C8yAvailability.self, forKey: .availability) diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift b/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift index 3140b41..83fc2f3 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift @@ -9,7 +9,7 @@ import Foundation public struct C8yAuditRecord: Codable { - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.activity = try container.decodeIfPresent(String.self, forKey: .activity) @@ -188,7 +188,7 @@ public struct C8yAuditRecord: Codable { } public struct C8yChanges: Codable { - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.attribute = try container.decodeIfPresent(String.self, forKey: .attribute) diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAuthConfig.swift b/Sources/CumulocityCoreLibrary/Model/C8yAuthConfig.swift index fef6afc..7fe34ba 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAuthConfig.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAuthConfig.swift @@ -48,7 +48,7 @@ public struct C8yAuthConfig: Codable { /// The name of the authentication provider. public var providerName: String? - /// SSO specific. URL used for redirecting to the Cumulocity IoT platform. + /// SSO specific. URL used for redirecting to the Cumulocity IoT platform. Do not set or leave it empty to allow SSO flow to be controlled by client (UI) applications. public var redirectToPlatform: String? public var refreshRequest: C8yRequestRepresentation? @@ -56,7 +56,7 @@ public struct C8yAuthConfig: Codable { /// A URL linking to this resource. public var `self`: String? - /// The session configuration properties are only available for OAuth internal. See [Changing settings > OAuth internal](https://cumulocity.com/guides/users-guide/administration/#oauth-internal) for more details. + /// The session configuration properties are only available for OAI-Secure. See [Platform administration > Authentication > Basic settings > OAI Secure session configuration ](https://cumulocity.com/docs/authentication/basic-settings/#oai-secure-session-configuration) in the Cumulocity IoT user documentation. public var sessionConfiguration: C8yOAuthSessionConfiguration? /// SSO specific and authorization server dependent. Describes the method of access token signature verification on the Cumulocity IoT platform. @@ -79,6 +79,9 @@ public struct C8yAuthConfig: Codable { /// Information for the UI if the respective authentication form should be visible for the user. public var visibleOnLoginPage: Bool? + /// A configuration for authentication with an access token from the authorization server. + public var externalTokenConfig: C8yExternalTokenConfig? + enum CodingKeys: String, CodingKey { case accessTokenToUserDataMapping case audience @@ -104,6 +107,7 @@ public struct C8yAuthConfig: Codable { case userIdConfig case userManagementSource case visibleOnLoginPage + case externalTokenConfig } public init(providerName: String, type: C8yType) { @@ -179,9 +183,13 @@ public struct C8yAuthConfig: Codable { /// Represents rules used to assign groups and applications. public var mappings: [C8yMappings]? + /// Represents rules used to assign inventory roles. + public var inventoryMappings: [C8yInventoryMappings]? + enum CodingKeys: String, CodingKey { case configuration case mappings + case inventoryMappings } public init() { @@ -193,8 +201,12 @@ public struct C8yAuthConfig: Codable { /// Indicates whether the mapping should be evaluated always or only during the first external login when the internal user is created. public var mapRolesOnlyForNewUser: Bool? + /// If set to `true`, dynamic access mapping is only managed for global roles, applications and inventory roles which are listed in the configuration. Others remain unchanged. + public var manageRolesOnlyFromAccessMapping: Bool? + enum CodingKeys: String, CodingKey { case mapRolesOnlyForNewUser + case manageRolesOnlyFromAccessMapping } public init() { @@ -204,7 +216,7 @@ public struct C8yAuthConfig: Codable { /// Represents information of mapping access to groups and applications. public struct C8yMappings: Codable { - /// Represents a predicate for verification. It acts as a condition which is necessary to assign a user to the given groups and permit access to the specified applications. + /// Represents a predicate for verification. It acts as a condition which is necessary to assign a user to the given groups, permit access to the specified applications or to assign specific inventory roles to device groups. public var when: C8yJSONPredicateRepresentation? /// List of the applications' identifiers. @@ -222,6 +234,42 @@ public struct C8yAuthConfig: Codable { public init() { } } + + /// Represents information of mapping access to inventory roles. + public struct C8yInventoryMappings: Codable { + + /// Represents a predicate for verification. It acts as a condition which is necessary to assign a user to the given groups, permit access to the specified applications or to assign specific inventory roles to device groups. + public var when: C8yJSONPredicateRepresentation? + + /// List of the OAuth inventory assignments. + public var thenInventoryRoles: [C8yThenInventoryRoles]? + + enum CodingKeys: String, CodingKey { + case when + case thenInventoryRoles + } + + public init() { + } + + /// Represents inventory roles for a specific device group. + public struct C8yThenInventoryRoles: Codable { + + /// A unique identifier for the managed object for which the roles are assigned. + public var managedObject: String? + + /// List of the inventory roles' identifiers. + public var roleIds: [Int]? + + enum CodingKeys: String, CodingKey { + case managedObject + case roleIds + } + + public init() { + } + } + } } } @@ -371,4 +419,66 @@ public struct C8yAuthConfig: Codable { } } + + /// A configuration for authentication with an access token from the authorization server. + public struct C8yExternalTokenConfig: Codable { + + /// Indicates whether authentication is enabled or disabled. + public var enabled: Bool? + + /// Points to the claim of the access token from the authorization server that must be used as the username in the Cumulocity IoT platform. + public var userOrAppIdConfig: C8yUserOrAppIdConfig? + + /// If set to `true`, the access token is validated against the authorization server by way of introspection or user info request. + public var validationRequired: Bool? + + /// The method of validation of the access token. + public var validationMethod: C8yValidationMethod? + + public var tokenValidationRequest: C8yRequestRepresentation? + + /// The frequency (in Minutes) in which Cumulocity sends a validation request to authorization server. The recommended frequency is 1 minute. + public var accessTokenValidityCheckIntervalInMinutes: Int? + + enum CodingKeys: String, CodingKey { + case enabled + case userOrAppIdConfig + case validationRequired + case validationMethod + case tokenValidationRequest + case accessTokenValidityCheckIntervalInMinutes + } + + public init() { + } + + /// The method of validation of the access token. + public enum C8yValidationMethod: String, Codable { + case introspection = "INTROSPECTION" + case userinfo = "USERINFO" + } + + /// Points to the claim of the access token from the authorization server that must be used as the username in the Cumulocity IoT platform. + public struct C8yUserOrAppIdConfig: Codable { + + /// Used only if `useConstantValue` is set to `true`. + public var constantValue: String? + + /// The name of the field containing the JWT. + public var jwtField: String? + + /// Not recommended. If set to `true`, all users share a single account in the Cumulocity IoT platform. + public var useConstantValue: Bool? + + enum CodingKeys: String, CodingKey { + case constantValue + case jwtField + case useConstantValue + } + + public init() { + } + } + + } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAvailability.swift b/Sources/CumulocityCoreLibrary/Model/C8yAvailability.swift index e5d5321..b011045 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAvailability.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAvailability.swift @@ -11,7 +11,7 @@ import Foundation /// The availability information computed by Cumulocity IoT is stored in fragments `c8y_Availability` and `c8y_Connection` of the device. public struct C8yAvailability: Codable { - /// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`. + /// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`, `UNAVAILABLE`. public var status: C8yAvailabilityStatus? /// The time when the device sent the last message to Cumulocity IoT. diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAvailabilityStatus.swift b/Sources/CumulocityCoreLibrary/Model/C8yAvailabilityStatus.swift index 50c0880..05ff6ed 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAvailabilityStatus.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAvailabilityStatus.swift @@ -8,10 +8,11 @@ import Foundation -/// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`. +/// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`, `UNAVAILABLE`. public enum C8yAvailabilityStatus: String, Codable { case available = "AVAILABLE" case connected = "CONNECTED" case maintenance = "MAINTENANCE" case disconnected = "DISCONNECTED" + case unavailable = "UNAVAILABLE" } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCRLEntry.swift b/Sources/CumulocityCoreLibrary/Model/C8yCRLEntry.swift new file mode 100644 index 0000000..f2e59c9 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yCRLEntry.swift @@ -0,0 +1,27 @@ +// +// C8yCRLEntry.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yCRLEntry: Codable { + + /// Revoked certificate serial number in hexadecimal. + public var serialNumberInHex: String? + + /// Date and time when the certificate is revoked. + public var revocationDate: String? + + enum CodingKeys: String, CodingKey { + case serialNumberInHex + case revocationDate + } + + public init(serialNumberInHex: String) { + self.serialNumberInHex = serialNumberInHex + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift b/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift index f12db29..403cb6d 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift @@ -9,7 +9,7 @@ import Foundation public struct C8yCategoryOptions: Codable { - + public init(from decoder: Decoder) throws { if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yCategoryOptions.decoders { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yConnection.swift b/Sources/CumulocityCoreLibrary/Model/C8yConnection.swift index 99477d4..766f59b 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yConnection.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yConnection.swift @@ -11,7 +11,7 @@ import Foundation /// The availability information computed by Cumulocity IoT is stored in fragments `c8y_Availability` and `c8y_Connection` of the device. public struct C8yConnection: Codable { - /// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`. + /// The current status, one of `AVAILABLE`, `CONNECTED`, `MAINTENANCE`, `DISCONNECTED`, `UNAVAILABLE`. public var status: C8yAvailabilityStatus? enum CodingKeys: String, CodingKey { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift b/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift index acd4286..ddb6890 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift @@ -10,7 +10,7 @@ import Foundation /// An object with a list of custom properties. public struct C8yCustomProperties: Codable { - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.language = try container.decodeIfPresent(String.self, forKey: .language) diff --git a/Sources/CumulocityCoreLibrary/Model/C8yDeviceCredentials.swift b/Sources/CumulocityCoreLibrary/Model/C8yDeviceCredentials.swift index 47008b6..a5e6d48 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yDeviceCredentials.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yDeviceCredentials.swift @@ -25,12 +25,16 @@ public struct C8yDeviceCredentials: Codable { /// Username of these device credentials. public var username: String? + /// Security token which is required and verified against during device request acceptance.See [Security token policy](https://cumulocity.com/docs/device-management-application/registering-devices/#security-token-policy) for more details on configuration.See [Update specific new device request status](/#operation/putNewDeviceRequestResource) for details on submitting token upon device acceptance. + public var securityToken: String? + enum CodingKeys: String, CodingKey { case id case password case `self` = "self" case tenantId case username + case securityToken } public init() { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissionOwners.swift b/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissionOwners.swift new file mode 100644 index 0000000..3f7e9c0 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissionOwners.swift @@ -0,0 +1,25 @@ +// +// C8yDevicePermissionOwners.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// A list of device permissions. +public struct C8yDevicePermissionOwners: Codable { + + public var users: [C8yUser]? + + public var groups: [C8yGroup]? + + enum CodingKeys: String, CodingKey { + case users + case groups + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissions.swift b/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissions.swift index 3136347..996b757 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissions.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yDevicePermissions.swift @@ -10,6 +10,36 @@ import Foundation /// An object with a list of the user's device permissions. public struct C8yDevicePermissions: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode([String].self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } + + public var additionalProperties: [String: [String]] = [:] + + public subscript(key: String) -> [String]? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } + + enum CodingKeys: String, CodingKey { + case additionalProperties + } public init() { } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift b/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift index 886d02d..d91ef2c 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift @@ -71,7 +71,7 @@ public struct C8yEvent: Codable { /// It is possible to add an arbitrary number of additional properties as a list of key-value pairs, for example, `"property1": {}`, `"property2": "value"`. These properties are known as custom fragments and can be of any type, for example, object or string. Each custom fragment is identified by a unique name. /// - /// Review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) as there are characters that can not be used when naming custom fragments. + /// Review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation as there are characters that can not be used when naming custom fragments. public var customFragments: [String: Any] = [:] public subscript(key: String) -> Any? { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggle.swift b/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggle.swift new file mode 100644 index 0000000..a93553a --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggle.swift @@ -0,0 +1,50 @@ +// +// C8yFeatureToggle.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yFeatureToggle: Codable { + + /// A unique key of the feature toggle. + public var key: String? + + /// Current phase of feature toggle rollout. + public var phase: C8yPhase? + + /// Current value of the feature toggle marking whether the feature is active or not. + public var active: Bool? + + /// The source of the feature toggle value - either it's feature toggle definition provided default, or per tenant provided override. + public var strategy: C8yStrategy? + + enum CodingKeys: String, CodingKey { + case key + case phase + case active + case strategy + } + + public init() { + } + + /// Current phase of feature toggle rollout. + public enum C8yPhase: String, Codable { + case indevelopment = "IN_DEVELOPMENT" + case privatepreview = "PRIVATE_PREVIEW" + case publicpreview = "PUBLIC_PREVIEW" + case generallyavailable = "GENERALLY_AVAILABLE" + } + + /// The source of the feature toggle value - either it's feature toggle definition provided default, or per tenant provided override. + public enum C8yStrategy: String, Codable { + case `default` = "DEFAULT" + case tenant = "TENANT" + } + + +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggleValue.swift b/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggleValue.swift new file mode 100644 index 0000000..6b54a25 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yFeatureToggleValue.swift @@ -0,0 +1,22 @@ +// +// C8yFeatureToggleValue.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yFeatureToggleValue: Codable { + + /// Current value of the feature toggle marking whether the feature is active or not. + public var active: Bool? + + enum CodingKeys: String, CodingKey { + case active + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yJSONPredicateRepresentation.swift b/Sources/CumulocityCoreLibrary/Model/C8yJSONPredicateRepresentation.swift index 255b22f..39b3b08 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yJSONPredicateRepresentation.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yJSONPredicateRepresentation.swift @@ -8,7 +8,7 @@ import Foundation -/// Represents a predicate for verification. It acts as a condition which is necessary to assign a user to the given groups and permit access to the specified applications. +/// Represents a predicate for verification. It acts as a condition which is necessary to assign a user to the given groups, permit access to the specified applications or to assign specific inventory roles to device groups. public struct C8yJSONPredicateRepresentation: Codable { /// Nested predicates. diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementFragment.swift b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementFragment.swift new file mode 100644 index 0000000..b7ef0ed --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementFragment.swift @@ -0,0 +1,48 @@ +// +// C8yLatestMeasurementFragment.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// The read only fragment which contains the latest measurements series reported by the device. +/// +/// > **⚠️ Feature Preview:** The feature is part of the Latest Measurement feature which is still under public feature preview. +public struct C8yLatestMeasurementFragment: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(C8yLatestMeasurementValue.self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } + + public var additionalProperties: [String: C8yLatestMeasurementValue] = [:] + + public subscript(key: String) -> C8yLatestMeasurementValue? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } + + enum CodingKeys: String, CodingKey { + case additionalProperties + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementValue.swift b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementValue.swift new file mode 100644 index 0000000..2355ddc --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurementValue.swift @@ -0,0 +1,34 @@ +// +// C8yLatestMeasurementValue.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// The read only fragment which contains the latest measurements series values reported by the device. +/// +/// > **������ Feature Preview:** The feature is part of the Latest Measurement feature which is still under public feature preview. +public struct C8yLatestMeasurementValue: Codable { + + /// The unit of the measurement series. + public var unit: String? + + /// The time of the measurement series. + public var time: String? + + /// The value of the individual measurement. + public var value: Double? + + enum CodingKeys: String, CodingKey { + case unit + case time + case value + } + + public init(value: Double) { + self.value = value + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurements.swift b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurements.swift new file mode 100644 index 0000000..5a7a7ac --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yLatestMeasurements.swift @@ -0,0 +1,48 @@ +// +// C8yLatestMeasurements.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// The read only fragment which contains the latest measurements reported by the device.The returned optionally only if the query parameter `withLatestValues=true` is used. +/// +/// > **⚠️ Feature Preview:** The feature is part of the Latest Measurement feature which is still under public feature preview. +public struct C8yLatestMeasurements: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(C8yLatestMeasurementFragment.self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } + + public var additionalProperties: [String: C8yLatestMeasurementFragment] = [:] + + public subscript(key: String) -> C8yLatestMeasurementFragment? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } + + enum CodingKeys: String, CodingKey { + case additionalProperties + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift b/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift index 838d9db..2b29cd5 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift @@ -16,13 +16,13 @@ public struct C8yLoginForm: Codable { /// Dependent on the authentication type. PASSWORD is used for OAI-Secure. public var grantType: C8yGrantType? - /// Used in cases of basic or OAI-Secure authentication. + /// Used in case of OAI-Secure authentication. public var password: String? - /// Current TFA code, sent by the user, if a TFA code is required to log in. + /// Current TFA code, sent by the user, if a TFA code is required to log in. Used in case of OAI-Secure authentication. public var tfaCode: String? - /// Used in cases of basic or OAI-Secure authentication. + /// Used in case of OAI-Secure authentication. public var username: String? enum CodingKeys: String, CodingKey { @@ -40,7 +40,6 @@ public struct C8yLoginForm: Codable { public enum C8yGrantType: String, Codable { case password = "PASSWORD" case authorizationcode = "AUTHORIZATION_CODE" - case refreshtoken = "REFRESH_TOKEN" } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLoginOption.swift b/Sources/CumulocityCoreLibrary/Model/C8yLoginOption.swift index 6c572f6..c5cad0c 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yLoginOption.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yLoginOption.swift @@ -35,7 +35,7 @@ public struct C8yLoginOption: Codable { /// A URL linking to this resource. public var `self`: String? - /// The session configuration properties are only available for OAuth internal. See [Changing settings > OAuth internal](https://cumulocity.com/guides/users-guide/administration/#oauth-internal) for more details. + /// The session configuration properties are only available for OAI-Secure. See [Platform administration > Authentication > Basic settings > OAI Secure session configuration ](https://cumulocity.com/docs/authentication/basic-settings/#oai-secure-session-configuration) in the Cumulocity IoT user documentation. public var sessionConfiguration: C8yOAuthSessionConfiguration? /// Enforce password strength validation on subtenant level. `enforceStrength` enforces it on all tenants in the platform. diff --git a/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift b/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift index a0a59df..73fda83 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift @@ -26,6 +26,8 @@ public struct C8yManagedObject: Codable { self.assetParents = try container.decodeIfPresent(C8yObjectAssetParents.self, forKey: .assetParents) self.deviceParents = try container.decodeIfPresent(C8yObjectDeviceParents.self, forKey: .deviceParents) self.c8yIsDevice = try container.decodeIfPresent(C8yIsDevice.self, forKey: .c8yIsDevice) + self.c8yLatestMeasurements = try container.decodeIfPresent(C8yLatestMeasurements.self, forKey: .c8yLatestMeasurements) + self.c8yIsDeviceGroup = try container.decodeIfPresent(C8yIsDeviceGroup.self, forKey: .c8yIsDeviceGroup) self.c8yDeviceTypes = try container.decodeIfPresent([String].self, forKey: .c8yDeviceTypes) self.c8ySupportedOperations = try container.decodeIfPresent([String].self, forKey: .c8ySupportedOperations) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { @@ -51,6 +53,8 @@ public struct C8yManagedObject: Codable { try container.encodeIfPresent(self.assetParents, forKey: .assetParents) try container.encodeIfPresent(self.deviceParents, forKey: .deviceParents) try container.encodeIfPresent(self.c8yIsDevice, forKey: .c8yIsDevice) + try container.encodeIfPresent(self.c8yLatestMeasurements, forKey: .c8yLatestMeasurements) + try container.encodeIfPresent(self.c8yIsDeviceGroup, forKey: .c8yIsDeviceGroup) try container.encodeIfPresent(self.c8yDeviceTypes, forKey: .c8yDeviceTypes) try container.encodeIfPresent(self.c8ySupportedOperations, forKey: .c8ySupportedOperations) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) @@ -103,6 +107,14 @@ public struct C8yManagedObject: Codable { /// A fragment which identifies this managed object as a device. public var c8yIsDevice: C8yIsDevice? + /// The read only fragment which contains the latest measurements reported by the device.The returned optionally only if the query parameter `withLatestValues=true` is used. + /// + /// > **������ Feature Preview:** The feature is part of the Latest Measurement feature which is still under public feature preview. + public var c8yLatestMeasurements: C8yLatestMeasurements? + + /// A fragment which identifies this managed object as a device group. + public var c8yIsDeviceGroup: C8yIsDeviceGroup? + /// This fragment must be added in order to publish sample commands for a subset of devices sharing the same device type. If the fragment is present, the list of sample commands for a device type will be extended with the sample commands for the `c8y_DeviceTypes`. New sample commands created from the user interface will be created in the context of the `c8y_DeviceTypes`. public var c8yDeviceTypes: [String]? @@ -111,7 +123,7 @@ public struct C8yManagedObject: Codable { /// It is possible to add an arbitrary number of additional properties as a list of key-value pairs, for example, `"property1": {}`, `"property2": "value"`. These properties are known as custom fragments and can be of any type, for example, object or string. Each custom fragment is identified by a unique name. /// - /// Review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) as there are characters that can not be used when naming custom fragments. + /// Review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation as there are characters that can not be used when naming custom fragments. public var customFragments: [String: Any] = [:] public subscript(key: String) -> Any? { @@ -138,6 +150,8 @@ public struct C8yManagedObject: Codable { case assetParents case deviceParents case c8yIsDevice = "c8y_IsDevice" + case c8yLatestMeasurements = "c8y_LatestMeasurements" + case c8yIsDeviceGroup = "c8y_IsDeviceGroup" case c8yDeviceTypes = "c8y_DeviceTypes" case c8ySupportedOperations = "c8y_SupportedOperations" case customFragments @@ -152,6 +166,13 @@ public struct C8yManagedObject: Codable { public init() { } } + + /// A fragment which identifies this managed object as a device group. + public struct C8yIsDeviceGroup: Codable { + + public init() { + } + } } extension C8yManagedObject { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift b/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift index 25c8738..452edc2 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift @@ -61,7 +61,7 @@ public struct C8yMeasurement: Codable { /// It is possible to add an arbitrary number of additional properties as a list of key-value pairs, for example, `"property1": {}`, `"property2": "value"`. These properties are known as custom fragments and can be of any type, for example, object or string. Each custom fragment is identified by a unique name. /// - /// Review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) as there are characters that can not be used when naming custom fragments. + /// Review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation as there are characters that can not be used when naming custom fragments. public var customFragments: [String: Any] = [:] public subscript(key: String) -> Any? { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeries.swift b/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeries.swift index 2ee8978..15d0ee5 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeries.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeries.swift @@ -30,6 +30,36 @@ public struct C8yMeasurementSeries: Codable { /// Each property contained here is a date taken from the measurement and it contains an array of objects specifying `min` and `max` pair of values. Each pair corresponds to a single series object in the `series` array. If there is no aggregation used, `min` is equal to `max` in every pair. public struct C8yValues: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode([C8yMeasurementSeriesValue].self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } + + public var additionalProperties: [String: [C8yMeasurementSeriesValue]] = [:] + + public subscript(key: String) -> [C8yMeasurementSeriesValue]? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } + + enum CodingKeys: String, CodingKey { + case additionalProperties + } public init() { } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeriesValue.swift b/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeriesValue.swift new file mode 100644 index 0000000..9cad61f --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yMeasurementSeriesValue.swift @@ -0,0 +1,24 @@ +// +// C8yMeasurementSeriesValue.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yMeasurementSeriesValue: Codable { + + public var min: Double? + + public var max: Double? + + enum CodingKeys: String, CodingKey { + case min + case max + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift b/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift index 466a84b..02d4ef5 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift @@ -10,6 +10,28 @@ import Foundation /// Holds basic connectivity-related information, such as the equipment identifier of the modem (IMEI) in the device. This identifier is globally unique and often used to identify a mobile device. public struct C8yMobile: Codable { + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.imei = try container.decodeIfPresent(String.self, forKey: .imei) + self.cellId = try container.decodeIfPresent(String.self, forKey: .cellId) + self.iccid = try container.decodeIfPresent(String.self, forKey: .iccid) + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(String.self, forKey: key) { + self.customFragments[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(self.imei, forKey: .imei) + try container.encodeIfPresent(self.cellId, forKey: .cellId) + try container.encodeIfPresent(self.iccid, forKey: .iccid) + try? container.encodeIfPresent(self.customFragments, forKey: .customFragments) + } /// The equipment identifier (IMEI) of the modem in the device. public var imei: String? diff --git a/Sources/CumulocityCoreLibrary/Model/C8yNewDeviceRequest.swift b/Sources/CumulocityCoreLibrary/Model/C8yNewDeviceRequest.swift index 6d98dc7..6ebec11 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yNewDeviceRequest.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yNewDeviceRequest.swift @@ -13,16 +13,40 @@ public struct C8yNewDeviceRequest: Codable { /// External ID of the device. public var id: String? + /// ID of the group to which the device will be assigned. + public var groupId: String? + + /// Type of the device. + public var type: String? + + /// Tenant who owns the device. + public var tenantId: String? + /// A URL linking to this resource. public var `self`: String? /// Status of this new device request. public var status: C8yStatus? + /// Owner of the device. + public var owner: String? + + /// Date and time when the device was created in the database. + public var creationTime: String? + + /// When accepting a device request, the security token is verified against the token submitted by the device when requesting credentials.See [Security token policy](https://cumulocity.com/docs/device-management-application/registering-devices/#security-token-policy) for details on configuration.See [Create device credentials](/#operation/postDeviceCredentialsCollectionResource) for details on creating token for device registration.`securityToken` parameter can be added only when submitting `ACCEPTED` status. + public var securityToken: String? + enum CodingKeys: String, CodingKey { case id + case groupId + case type + case tenantId case `self` = "self" case status + case owner + case creationTime + case securityToken } public init() { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yNotificationSubscription.swift b/Sources/CumulocityCoreLibrary/Model/C8yNotificationSubscription.swift index 68d3d2d..802f557 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yNotificationSubscription.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yNotificationSubscription.swift @@ -12,7 +12,7 @@ public struct C8yNotificationSubscription: Codable { /// The context within which the subscription is to be processed. /// - /// > **ⓘ Note** If the value is `mo`, then `source` must also be provided in the request body. + /// > **ⓘ Note** If the value is `mo` (managed object), then `source` must also be provided in the request body. public var context: C8yContext? /// Transforms the data to *only* include specified custom fragments. Each custom fragment is identified by a unique name. If nothing is specified here, the data is forwarded as-is. @@ -33,6 +33,9 @@ public struct C8yNotificationSubscription: Codable { /// Applicable filters to the subscription. public var subscriptionFilter: C8ySubscriptionFilter? + /// Indicates whether the messages for this subscription are persistent or non-persistent, meaning they can be lost if consumer is not connected. + public var nonPersistent: Bool? + enum CodingKeys: String, CodingKey { case context case fragmentsToCopy @@ -41,6 +44,7 @@ public struct C8yNotificationSubscription: Codable { case source case subscription case subscriptionFilter + case nonPersistent } public init(context: C8yContext, subscription: String) { @@ -50,7 +54,7 @@ public struct C8yNotificationSubscription: Codable { /// The context within which the subscription is to be processed. /// - /// > **ⓘ Note** If the value is `mo`, then `source` must also be provided in the request body. + /// > **ⓘ Note** If the value is `mo` (managed object), then `source` must also be provided in the request body. public enum C8yContext: String, Codable { case mo = "mo" case tenant = "tenant" @@ -82,12 +86,20 @@ public struct C8yNotificationSubscription: Codable { /// Applicable filters to the subscription. public struct C8ySubscriptionFilter: Codable { - /// The Notifications are available for Alarms, Alarms with children, Device control, Events, Events with children, Inventory and Measurements for the `mo` context and for Alarms and Inventory for the `tenant` context. Alternatively, the wildcard `*` can be used to match all the permissible APIs within the bound context. + /// For the `mo` (managed object) context, notifications from the `alarms`, `alarmsWithChildren`, `events`, `eventsWithChildren`, `managedobjects` (Inventory), `measurements` and `operations` (Device control) APIs can be subscribed to.The `alarmsWithChildren` and `eventsWithChildren` APIs subscribe to alarms and events respectively from the managed object identified by the `source.id` field, and all of its descendant managed objects. + /// + /// For the `tenant` context, notifications from the `alarms`, `events` and `managedobjects` (Inventory) APIs can be subscribed to. /// - /// > **ⓘ Note** the wildcard `*` cannot be used in conjunction with other values. + /// For all contexts, the `*` (wildcard) value can be used to subscribe to notifications from all of the available APIs in that context. + /// + /// > **ⓘ Note** The wildcard `*` cannot be used in conjunction with other values. + /// > **ⓘ Note** When filtering Events in the `tenant` context it is required to also specify the `typeFilter`. public var apis: [String]? - /// The data needs to have the specified value in its `type` property to meet the filter criteria. + /// Used to match the `type` property of the data. This must either be a string to match one specific type exactly, or be an `or` OData expression, allowing the filter to match any one of a number of types. + /// + /// > **ⓘ Note** The use of a `type` attribute is assumed, for example when using only a string literal `'c8y_Temperature'` (or using `c8y_Temperature`, as quotes can be omitted when matching a single type) it is equivalent to a `type eq 'c8y_Temperature'` OData expression. + /// > **ⓘ Note** Currently only the `or` operator is allowed when using an OData expression. Example usage is `'c8y_Temperature' or 'c8y_Pressure'` which will match all the data with types `c8y_Temperature` or `c8y_Pressure`. public var typeFilter: String? enum CodingKeys: String, CodingKey { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yNotificationTokenClaims.swift b/Sources/CumulocityCoreLibrary/Model/C8yNotificationTokenClaims.swift index c1a59d9..93fc4fb 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yNotificationTokenClaims.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yNotificationTokenClaims.swift @@ -19,14 +19,36 @@ public struct C8yNotificationTokenClaims: Codable { /// The subscription name. This value must match the same that was used when the subscription was created. public var subscription: String? + /// The subscription type. Currently the only supported type is `notification` .Other types may be added in future. + public var type: C8yType? + + /// If `true`, the token will be securely signed by the Cumulocity IoT platform. + public var signed: Bool? + + /// If `true`, indicates that the token is used to create a shared consumer on the subscription. + public var shared: Bool? + + /// If `true`, indicates that the created token refers to the non-persistent variant of the named subscription. + public var nonPersistent: Bool? + enum CodingKeys: String, CodingKey { case expiresInMinutes case subscriber case subscription + case type + case signed + case shared + case nonPersistent } public init(subscriber: String, subscription: String) { self.subscriber = subscriber self.subscription = subscription } + + /// The subscription type. Currently the only supported type is `notification` .Other types may be added in future. + public enum C8yType: String, Codable { + case notification = "notification" + } + } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yOAuthSessionConfiguration.swift b/Sources/CumulocityCoreLibrary/Model/C8yOAuthSessionConfiguration.swift index e281791..01a81a2 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yOAuthSessionConfiguration.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yOAuthSessionConfiguration.swift @@ -8,7 +8,7 @@ import Foundation -/// The session configuration properties are only available for OAuth internal. See [Changing settings > OAuth internal](https://cumulocity.com/guides/users-guide/administration/#oauth-internal) for more details. +/// The session configuration properties are only available for OAI-Secure. See [Platform administration > Authentication > Basic settings > OAI Secure session configuration ](https://cumulocity.com/docs/authentication/basic-settings/#oai-secure-session-configuration) in the Cumulocity IoT user documentation. public struct C8yOAuthSessionConfiguration: Codable { /// Maximum session duration (in milliseconds) during which a user does not have to login again. diff --git a/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift b/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift index 91e7a23..edb7072 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift @@ -70,7 +70,7 @@ public struct C8yOperation: Codable { /// It is possible to add an arbitrary number of additional properties as a list of key-value pairs, for example, `"property1": {}`, `"property2": "value"`. These properties are known as custom fragments and can be of any type, for example, object or string. Each custom fragment is identified by a unique name. /// - /// Review the [Naming conventions of fragments](https://cumulocity.com/guides/concepts/domain-model/#naming-conventions-of-fragments) as there are characters that can not be used when naming custom fragments. + /// Review [Getting started > Technical concepts > Cumulocity IoT's domain model > Inventory > Fragments > Naming conventions of fragments](https://cumulocity.com/docs/concepts/domain-model/#naming-conventions-of-fragments) in the Cumulocity IoT user documentation as there are characters that can not be used when naming custom fragments. public var customFragments: [String: Any] = [:] public subscript(key: String) -> Any? { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yPageStatistics.swift b/Sources/CumulocityCoreLibrary/Model/C8yPageStatistics.swift index 586be14..0db8ce0 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yPageStatistics.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yPageStatistics.swift @@ -22,7 +22,7 @@ public struct C8yPageStatistics: Codable { /// The total number of paginated results (pages). /// - /// > **ⓘ Note** This property is returned by default except when an operation retrieves all records where values are between an upper and lower boundary, for example, querying ranges using `dateFrom`–`dateTo`. In such cases, the query parameter `withTotalPages=true` should be used to include the total number of pages (at the expense of slightly slower performance). + /// > **ⓘ Note** This property is returned by default except when an operation retrieves all records where values are between an upper and lower boundary, for example, querying ranges using `dateFrom`���`dateTo`. In such cases, the query parameter `withTotalPages=true` should be used to include the total number of pages (at the expense of slightly slower performance). public var totalPages: Int? enum CodingKeys: String, CodingKey { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift b/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift index b146680..642b309 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift @@ -54,6 +54,21 @@ public struct C8yRequestRepresentation: Codable { /// Headers of the request. public struct C8yHeaders: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(String.self, forKey: key) { + self.requestHeaders[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.requestHeaders, forKey: .requestHeaders) + } /// It is possible to add an arbitrary number of headers as a list of key-value string pairs, for example, `"header": "value"`. public var requestHeaders: [String: String] = [:] @@ -79,6 +94,21 @@ public struct C8yRequestRepresentation: Codable { /// Parameters of the request. public struct C8yRequestParams: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(String.self, forKey: key) { + self.requestParameters[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.requestParameters, forKey: .requestParameters) + } /// It is possible to add an arbitrary number of parameters as a list of key-value string pairs, for example, `"parameter": "value"`. public var requestParameters: [String: String] = [:] diff --git a/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift b/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift index d6435b3..d11a5c1 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift @@ -10,6 +10,21 @@ import Foundation /// Measurement of the single phase energy meter. public struct C8ySinglePhaseEnergyMeasurement: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(C8yMeasurementValue.self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } public var additionalProperties: [String: C8yMeasurementValue] = [:] diff --git a/Sources/CumulocityCoreLibrary/Model/C8yTenantFeatureToggleValue.swift b/Sources/CumulocityCoreLibrary/Model/C8yTenantFeatureToggleValue.swift new file mode 100644 index 0000000..32767a8 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yTenantFeatureToggleValue.swift @@ -0,0 +1,26 @@ +// +// C8yTenantFeatureToggleValue.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yTenantFeatureToggleValue: Codable { + + /// Identifier of a tenant this feature toggle value is for. + public var tenantId: String? + + /// Current value of the feature toggle marking whether the feature is active or not. + public var active: Bool? + + enum CodingKeys: String, CodingKey { + case tenantId + case active + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaStrategy.swift b/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaStrategy.swift new file mode 100644 index 0000000..982a0d9 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaStrategy.swift @@ -0,0 +1,30 @@ +// +// C8yTenantTfaStrategy.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yTenantTfaStrategy: Codable { + + /// Two-factor authentication strategy. + public var strategy: C8yStrategy? + + enum CodingKeys: String, CodingKey { + case strategy + } + + public init(strategy: C8yStrategy) { + self.strategy = strategy + } + + /// Two-factor authentication strategy. + public enum C8yStrategy: String, Codable { + case sms = "SMS" + case totp = "TOTP" + } + +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift b/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift index 828073e..6fb5832 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift @@ -10,6 +10,21 @@ import Foundation /// Measurement of the three phase energy meter. public struct C8yThreePhaseEnergyMeasurement: Codable { + + public init(from decoder: Decoder) throws { + if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { + for key in additionalContainer.allKeys { + if let value = try? additionalContainer.decode(C8yMeasurementValue.self, forKey: key) { + self.additionalProperties[key.stringValue] = value + } + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try? container.encodeIfPresent(self.additionalProperties, forKey: .additionalProperties) + } public var additionalProperties: [String: C8yMeasurementValue] = [:] diff --git a/Sources/CumulocityCoreLibrary/Model/C8yTrustedCertificate.swift b/Sources/CumulocityCoreLibrary/Model/C8yTrustedCertificate.swift index 158dc7e..be5b5ad 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yTrustedCertificate.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yTrustedCertificate.swift @@ -34,6 +34,15 @@ public struct C8yTrustedCertificate: Codable { /// The start date and time of the certificate's validity. public var notBefore: String? + /// Indicates whether the proof of possession for the certificate was provided. + public var proofOfPossessionValid: Bool? + + /// An unsigned verification code that provides proof of possession for the certificate after signing. + public var proofOfPossessionUnsignedVerificationCode: String? + + /// Validity of the verification code. + public var proofOfPossessionVerificationCodeUsableUntil: String? + /// A URL linking to this resource. public var `self`: String? @@ -58,6 +67,9 @@ public struct C8yTrustedCertificate: Codable { case name case notAfter case notBefore + case proofOfPossessionValid + case proofOfPossessionUnsignedVerificationCode + case proofOfPossessionVerificationCodeUsableUntil case `self` = "self" case serialNumber case status diff --git a/Sources/CumulocityCoreLibrary/Api/LoginTokensApi.swift b/Sources/CumulocityCoreLibrary/Model/C8yUpdateCRLEntries.swift similarity index 64% rename from Sources/CumulocityCoreLibrary/Api/LoginTokensApi.swift rename to Sources/CumulocityCoreLibrary/Model/C8yUpdateCRLEntries.swift index 26059ef..af249c8 100644 --- a/Sources/CumulocityCoreLibrary/Api/LoginTokensApi.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yUpdateCRLEntries.swift @@ -1,5 +1,5 @@ // -// LoginTokensApi.swift +// C8yUpdateCRLEntries.swift // CumulocityCoreLibrary // // Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. @@ -7,9 +7,16 @@ // import Foundation -import Combine -/// API methods to obtain access tokens to the Cumulocity IoT platform in case of OAI-Secure or SSO authentication. -public class LoginTokensApi: AdaptableApi { +/// A list of serial numbers. +public struct C8yUpdateCRLEntries: Codable { + public var crls: [C8yCRLEntry]? + + enum CodingKeys: String, CodingKey { + case crls + } + + public init() { + } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUpdatedDevicePermissions.swift b/Sources/CumulocityCoreLibrary/Model/C8yUpdatedDevicePermissions.swift new file mode 100644 index 0000000..fd7ea35 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yUpdatedDevicePermissions.swift @@ -0,0 +1,59 @@ +// +// C8yUpdatedDevicePermissions.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// A list of device permissions. +public struct C8yUpdatedDevicePermissions: Codable { + + public var users: [C8yUsers]? + + public var groups: [C8yGroups]? + + enum CodingKeys: String, CodingKey { + case users + case groups + } + + public init() { + } + + public struct C8yUsers: Codable { + + public var userName: String? + + /// An object with a list of the user's device permissions. + @available(*, deprecated) + public var devicePermissions: C8yDevicePermissions? + + enum CodingKeys: String, CodingKey { + case userName + case devicePermissions + } + + public init() { + } + } + + public struct C8yGroups: Codable { + + public var id: String? + + /// An object with a list of the user's device permissions. + @available(*, deprecated) + public var devicePermissions: C8yDevicePermissions? + + enum CodingKeys: String, CodingKey { + case id + case devicePermissions + } + + public init() { + } + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift index fd10660..e3d63a6 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift @@ -18,6 +18,7 @@ public struct C8yUploadedTrustedCertSignedVerificationCode: Codable { case proofOfPossessionSignedVerificationCode } - public init() { + public init(proofOfPossessionSignedVerificationCode: String) { + self.proofOfPossessionSignedVerificationCode = proofOfPossessionSignedVerificationCode } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificate.swift b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificate.swift new file mode 100644 index 0000000..3735111 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificate.swift @@ -0,0 +1,43 @@ +// +// C8yUploadedTrustedCertificate.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yUploadedTrustedCertificate: Codable { + + /// Indicates whether the automatic device registration is enabled or not. + public var autoRegistrationEnabled: Bool? + + /// Trusted certificate in PEM format. + public var certInPemFormat: String? + + /// Name of the certificate. + public var name: String? + + /// Indicates if the certificate is active and can be used by the device to establish a connection to the Cumulocity IoT platform. + public var status: C8yStatus? + + enum CodingKeys: String, CodingKey { + case autoRegistrationEnabled + case certInPemFormat + case name + case status + } + + public init(certInPemFormat: String, status: C8yStatus) { + self.certInPemFormat = certInPemFormat + self.status = status + } + + /// Indicates if the certificate is active and can be used by the device to establish a connection to the Cumulocity IoT platform. + public enum C8yStatus: String, Codable { + case enabled = "ENABLED" + case disabled = "DISABLED" + } + +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificateCollection.swift b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificateCollection.swift new file mode 100644 index 0000000..75f09d2 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertificateCollection.swift @@ -0,0 +1,38 @@ +// +// C8yUploadedTrustedCertificateCollection.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +/// A collection of uploaded trusted certificates. +public struct C8yUploadedTrustedCertificateCollection: Codable { + + public var certificates: [C8yUploadedTrustedCertificate]? + + /// A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential next page of managed objects. + public var next: String? + + /// A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential previous page of managed objects. + public var prev: String? + + /// A URL linking to this resource. + public var `self`: String? + + /// Information about paging statistics. + public var statistics: C8yPageStatistics? + + enum CodingKeys: String, CodingKey { + case certificates + case next + case prev + case `self` = "self" + case statistics + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yVerifyCertificateChain.swift b/Sources/CumulocityCoreLibrary/Model/C8yVerifyCertificateChain.swift new file mode 100644 index 0000000..1172fcc --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yVerifyCertificateChain.swift @@ -0,0 +1,34 @@ +// +// C8yVerifyCertificateChain.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. +// Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG. +// + +import Foundation + +public struct C8yVerifyCertificateChain: Codable { + + /// The result of validating the certificate chain. + public var successfullyValidated: Bool? + + /// The tenant ID used for validation. + public var tenantId: String? + + /// The name of the organization which signed the certificate. + public var issuer: String? + + /// The name of the organization to which the certificate belongs. + public var subject: String? + + enum CodingKeys: String, CodingKey { + case successfullyValidated + case tenantId + case issuer + case subject + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Supplementary/BadResponseError.swift b/Sources/CumulocityCoreLibrary/Supplementary/BadResponseError.swift index 2a8b021..641ce02 100644 --- a/Sources/CumulocityCoreLibrary/Supplementary/BadResponseError.swift +++ b/Sources/CumulocityCoreLibrary/Supplementary/BadResponseError.swift @@ -45,7 +45,7 @@ public class BadResponseErrorAwareData : BadResponseError { public extension Subscribers.Completion { - public func error() throws -> BadResponseError? { + func error() throws -> BadResponseError? { if case .failure(let failure) = self { if let error = failure as? BadResponseError { return error diff --git a/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift b/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift index c342b31..c3300f6 100644 --- a/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift +++ b/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift @@ -25,12 +25,12 @@ extension Cumulocity { public lazy var tenants: TenantsFactory = TenantsFactory(with: self) public lazy var users: UsersFactory = UsersFactory(with: self) public lazy var audits: AuditsFactory = AuditsFactory(with: self) - public lazy var realtimeNotifications: RealtimenotificationsFactory = RealtimenotificationsFactory(with: self) + public lazy var realtimeNotifications: RealtimeNotificationsFactory = RealtimeNotificationsFactory(with: self) public lazy var events: EventsFactory = EventsFactory(with: self) public lazy var notifications20: Notifications20Factory = Notifications20Factory(with: self) public lazy var retentions: RetentionsFactory = RetentionsFactory(with: self) public lazy var identity: IdentityFactory = IdentityFactory(with: self) - public lazy var deviceControl: DevicecontrolFactory = DevicecontrolFactory(with: self) + public lazy var deviceControl: DeviceControlFactory = DeviceControlFactory(with: self) public lazy var inventory: InventoryFactory = InventoryFactory(with: self) private init() { @@ -84,7 +84,6 @@ extension Cumulocity { public lazy var usageStatisticsApi: UsageStatisticsApi = UsageStatisticsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var optionsApi: OptionsApi = OptionsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var loginOptionsApi: LoginOptionsApi = LoginOptionsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) - public lazy var loginTokensApi: LoginTokensApi = LoginTokensApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var systemOptionsApi: SystemOptionsApi = SystemOptionsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) fileprivate init(with factory: Core) { @@ -101,6 +100,7 @@ extension Cumulocity { public lazy var groupsApi: GroupsApi = GroupsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var rolesApi: RolesApi = RolesApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var inventoryRolesApi: InventoryRolesApi = InventoryRolesApi(requestBuilder: factory.requestBuilder, withSession: factory.session) + public lazy var devicePermissionsApi: DevicePermissionsApi = DevicePermissionsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) fileprivate init(with factory: Core) { self.factory = factory @@ -118,7 +118,7 @@ extension Cumulocity { } } - public class RealtimenotificationsFactory { + public class RealtimeNotificationsFactory { private var factory: Core @@ -176,7 +176,7 @@ extension Cumulocity { } } - public class DevicecontrolFactory { + public class DeviceControlFactory { private var factory: Core diff --git a/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift b/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift index d454046..e2df668 100644 --- a/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift +++ b/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift @@ -26,7 +26,7 @@ public class URLRequestBuilder { } public convenience init(with: URLRequestBuilder) { - self.init() + self.init() self.components = with.components self.httpMethod = with.httpMethod self.requestHeaders = with.requestHeaders diff --git a/Tests/CumulocityCoreLibraryTests/Api/LoginTokensApiTest.swift b/Tests/CumulocityCoreLibraryTests/Api/DevicePermissionsApiTest.swift similarity index 87% rename from Tests/CumulocityCoreLibraryTests/Api/LoginTokensApiTest.swift rename to Tests/CumulocityCoreLibraryTests/Api/DevicePermissionsApiTest.swift index 469feef..fa4ec9d 100644 --- a/Tests/CumulocityCoreLibraryTests/Api/LoginTokensApiTest.swift +++ b/Tests/CumulocityCoreLibraryTests/Api/DevicePermissionsApiTest.swift @@ -1,5 +1,5 @@ // -// LoginTokensApiTest.swift +// DevicePermissionsApiTest.swift // CumulocityCoreLibrary // // Copyright (c) 2014-2023 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors. @@ -11,9 +11,9 @@ import XCTest @testable import CumulocityCoreLibrary -public class LoginTokensApiTest: XCTestCase { +public class DevicePermissionsApiTest: XCTestCase { - class TestableLoginTokensApi: LoginTokensApi { + class TestableDevicePermissionsApi: DevicePermissionsApi { override func adapt(builder: URLRequestBuilder) -> URLRequestBuilder { guard let testDataUrl = Bundle.module.path(forResource: "TestData", ofType: "plist") else { return builder } diff --git a/Tests/CumulocityCoreLibraryTests/Api/ManagedObjectsApiTest.swift b/Tests/CumulocityCoreLibraryTests/Api/ManagedObjectsApiTest.swift index ffcde60..6c8254e 100644 --- a/Tests/CumulocityCoreLibraryTests/Api/ManagedObjectsApiTest.swift +++ b/Tests/CumulocityCoreLibraryTests/Api/ManagedObjectsApiTest.swift @@ -39,17 +39,4 @@ public class ManagedObjectsApiTest: XCTestCase { }).store(in: &cancellables) wait(for: [expectation], timeout: 10) } - - public func testGetNumberOfManagedObjects() { - let expectation = XCTestExpectation(description: "ok") - var cancellables = Set() - TestableManagedObjectsApi().getNumberOfManagedObjects().sink(receiveCompletion: { completion in - let message = try? completion.error() - print(message?.httpResponse?.statusCode ?? "Successfully") - }, receiveValue: { data in - expectation.fulfill() - print(data) - }).store(in: &cancellables) - wait(for: [expectation], timeout: 10) - } } diff --git a/Tests/CumulocityCoreLibraryTests/Api/TrustedCertificatesApiTest.swift b/Tests/CumulocityCoreLibraryTests/Api/TrustedCertificatesApiTest.swift index 0140e4a..7354e65 100644 --- a/Tests/CumulocityCoreLibraryTests/Api/TrustedCertificatesApiTest.swift +++ b/Tests/CumulocityCoreLibraryTests/Api/TrustedCertificatesApiTest.swift @@ -27,4 +27,16 @@ public class TrustedCertificatesApiTest: XCTestCase { } } + public func testDownloadCrl() { + let expectation = XCTestExpectation(description: "ok") + var cancellables = Set() + TestableTrustedCertificatesApi().downloadCrl().sink(receiveCompletion: { completion in + let message = try? completion.error() + print(message?.httpResponse?.statusCode ?? "Successfully") + }, receiveValue: { data in + expectation.fulfill() + print(data) + }).store(in: &cancellables) + wait(for: [expectation], timeout: 10) + } }