From 9f6efe68d4466948d3f1074d70168e2a9e1eb596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hartmann?= Date: Wed, 9 Nov 2022 11:34:16 +0100 Subject: [PATCH] Reworked error handling and removed throws declaration from REST functions. Added subscript methods for structs using dictionaries. --- README.md | 15 + .../CumulocityCoreLibrary/Api/AlarmsApi.swift | 144 ++--- .../Api/ApplicationBinariesApi.swift | 58 +- .../Api/ApplicationsApi.swift | 165 ++---- .../Api/AttachmentsApi.swift | 111 ++-- .../CumulocityCoreLibrary/Api/AuditsApi.swift | 44 +- .../Api/BinariesApi.swift | 76 +-- .../Api/BootstrapUserApi.swift | 17 +- .../Api/BulkOperationsApi.swift | 94 ++-- .../Api/ChildOperationsApi.swift | 507 ++++++------------ .../Api/CurrentApplicationApi.swift | 71 +-- .../Api/CurrentUserApi.swift | 247 ++++++++- .../Api/DeviceCredentialsApi.swift | 34 +- .../Api/DeviceStatisticsApi.swift | 30 +- .../CumulocityCoreLibrary/Api/EventsApi.swift | 122 ++--- .../Api/ExternalIDsApi.swift | 69 +-- .../CumulocityCoreLibrary/Api/GroupsApi.swift | 167 ++---- .../Api/IdentityApi.swift | 12 +- .../Api/InventoryApi.swift | 4 +- .../Api/InventoryRolesApi.swift | 230 +++----- .../Api/LoginOptionsApi.swift | 38 +- .../Api/ManagedObjectsApi.swift | 204 +++---- .../Api/MeasurementsApi.swift | 132 ++--- .../Api/NewDeviceRequestsApi.swift | 96 ++-- .../Api/OperationsApi.swift | 100 ++-- .../Api/OptionsApi.swift | 126 ++--- .../Api/RealtimeNotificationApi.swift | 23 +- .../Api/RetentionRulesApi.swift | 112 ++-- .../CumulocityCoreLibrary/Api/RolesApi.swift | 158 ++---- .../Api/SubscriptionsApi.swift | 119 +--- .../Api/SystemOptionsApi.swift | 24 +- .../Api/TenantApplicationsApi.swift | 65 +-- .../Api/TenantsApi.swift | 159 +++--- .../CumulocityCoreLibrary/Api/TokensApi.swift | 28 +- .../Api/TrustedCertificatesApi.swift | 266 ++++++--- .../Api/UsageStatisticsApi.swift | 100 ++-- .../CumulocityCoreLibrary/Api/UsersApi.swift | 330 ++++++------ .../Model/C8yAlarm.swift | 15 +- .../Model/C8yAuditRecord.swift | 15 +- .../Model/C8yCategoryOptions.swift | 15 +- .../Model/C8yCurrentUser.swift | 4 + .../Model/C8yCurrentUserTotpCode.swift | 23 + .../Model/C8yCurrentUserTotpSecret.swift | 26 + .../C8yCurrentUserTotpSecretActivity.swift | 23 + .../Model/C8yCustomProperties.swift | 15 +- .../Model/C8yEvent.swift | 15 +- .../Model/C8yLoginForm.swift | 2 +- .../Model/C8yManagedObject.swift | 15 +- .../Model/C8yMeasurement.swift | 15 +- .../C8yMicroserviceApplicationManifest.swift | 45 +- .../Model/C8yMobile.swift | 11 +- .../Model/C8yOperation.swift | 28 +- .../Model/C8yPasswordChange.swift | 28 + .../Model/C8yRequestRepresentation.swift | 22 +- .../C8ySinglePhaseEnergyMeasurement.swift | 11 +- .../Model/C8yTenantTfaData.swift | 49 ++ .../C8yThreePhaseEnergyMeasurement.swift | 11 +- ...dedTrustedCertSignedVerificationCode.swift | 23 + .../CumulocityCoreLibrary/Model/C8yUser.swift | 4 + .../Model/C8yUserTfaData.swift | 41 ++ .../Supplementary/CumulocityCoreLibrary.swift | 1 - .../Supplementary/URLRequestBuilder.swift | 2 +- .../Api/CurrentUserApiTest.swift | 13 + .../Api/InventoryApiTest.swift | 42 -- 64 files changed, 2105 insertions(+), 2706 deletions(-) create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpCode.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecret.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecretActivity.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yPasswordChange.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yTenantTfaData.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift create mode 100644 Sources/CumulocityCoreLibrary/Model/C8yUserTfaData.swift delete mode 100644 Tests/CumulocityCoreLibraryTests/Api/InventoryApiTest.swift diff --git a/README.md b/README.md index 21ab866..0322111 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,21 @@ The `URLSession` may also be passed to individual `API` classes through it's ini let api = ApplicationsApi(session: session) ``` +### Use your own domain model + +The CumulocityCoreLibrary allows custom data models. The following classes are designed to be extendible: + +- `C8yAlarm`, `C8yAuditRecord`, `C8yCategoryOptions`, `C8yCustomProperties`, `C8yEvent`, `C8yManagedObject`, `C8yMeasurement`, `C8yOperation` + +Those classes allow to add an arbitrary number of additional properties as a list of key-value pairs. These properties are known as custom fragments and can be of any type. Each custom fragment is identified by a unique name. Thus, developers can propagate their custom fragments using: + +```swift +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. + +In addition, developers may create subclasses. The client implementation respects subclassing by using generic type arguments. ### Working with errors Use `sink(receiveCompletion:receiveValue:)` to observe values received by the publisher and process them using a closure you specify. HTTP error codes will be forwarded and can be accessed using a completion handler. The client describes two error types: diff --git a/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift b/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift index b8cdc99..42707d7 100644 --- a/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AlarmsApi.swift @@ -16,7 +16,7 @@ import Combine public class AlarmsApi: AdaptableApi { /// Retrieve all alarms - /// Retrieve all alarms on your tenant, or a specific subset based on queries. + /// Retrieve all alarms on your tenant, or a specific subset based on queries. The results are sorted by the newest alarms first. /// /// #### Query parameters /// @@ -67,7 +67,7 @@ public class AlarmsApi: 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 getAlarms(createdFrom: String? = nil, createdTo: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, lastUpdatedFrom: String? = nil, lastUpdatedTo: String? = nil, pageSize: Int? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getAlarms(createdFrom: String? = nil, createdTo: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, lastUpdatedFrom: String? = nil, lastUpdatedTo: String? = nil, pageSize: Int? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = createdFrom { queryItems.append(URLQueryItem(name: "createdFrom", value: String(parameter))) } if let parameter = createdTo { queryItems.append(URLQueryItem(name: "createdTo", value: String(parameter))) } @@ -95,16 +95,12 @@ public class AlarmsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAlarmCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -153,7 +149,7 @@ public class AlarmsApi: AdaptableApi { /// When set to `true` also alarms for related source assets will be included in the request. When this parameter is provided a `source` must be specified. /// - withSourceDevices /// When set to `true` also alarms for related source devices will be included in the request. When this parameter is provided a `source` must be specified. - public func updateAlarms(body: C8yAlarm, createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) throws -> AnyPublisher { + public func updateAlarms(body: C8yAlarm, createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = createdFrom { queryItems.append(URLQueryItem(name: "createdFrom", value: String(parameter))) } if let parameter = createdTo { queryItems.append(URLQueryItem(name: "createdTo", value: String(parameter))) } @@ -177,33 +173,29 @@ public class AlarmsApi: AdaptableApi { requestBody.text = nil requestBody.time = nil requestBody.type = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/alarm/alarms") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.alarm+json") .add(header: "Accept", value: "application/json") .set(queryItems: queryItems) - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -248,7 +240,7 @@ public class AlarmsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createAlarm(body: C8yAlarm) throws -> AnyPublisher { + public func createAlarm(body: C8yAlarm) -> AnyPublisher { var requestBody = body requestBody.firstOccurrenceTime = nil requestBody.lastUpdated = nil @@ -257,32 +249,28 @@ public class AlarmsApi: AdaptableApi { requestBody.`self` = nil requestBody.id = nil requestBody.source?.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/alarm/alarms") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.alarm+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.alarm+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAlarm.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -328,7 +316,7 @@ public class AlarmsApi: AdaptableApi { /// When set to `true` also alarms for related source assets will be included in the request. When this parameter is provided a `source` must be specified. /// - withSourceDevices /// When set to `true` also alarms for related source devices will be included in the request. When this parameter is provided a `source` must be specified. - public func deleteAlarms(createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) throws -> AnyPublisher { + public func deleteAlarms(createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = createdFrom { queryItems.append(URLQueryItem(name: "createdFrom", value: String(parameter))) } if let parameter = createdTo { queryItems.append(URLQueryItem(name: "createdTo", value: String(parameter))) } @@ -350,19 +338,12 @@ public class AlarmsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -387,7 +368,7 @@ public class AlarmsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the alarm. - public func getAlarm(id: String) throws -> AnyPublisher { + public func getAlarm(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/alarm/alarms/\(id)") .set(httpMethod: "get") @@ -396,24 +377,12 @@ public class AlarmsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAlarm.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -444,7 +413,7 @@ public class AlarmsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the alarm. - public func updateAlarm(body: C8yAlarm, id: String) throws -> AnyPublisher { + public func updateAlarm(body: C8yAlarm, id: String) -> AnyPublisher { var requestBody = body requestBody.firstOccurrenceTime = nil requestBody.lastUpdated = nil @@ -455,37 +424,28 @@ public class AlarmsApi: AdaptableApi { requestBody.source = nil requestBody.time = nil requestBody.type = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/alarm/alarms/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.alarm+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.alarm+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAlarm.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -522,7 +482,7 @@ public class AlarmsApi: AdaptableApi { /// When set to `true` also alarms for related source assets will be included in the request. When this parameter is provided a `source` must be specified. /// - withSourceDevices /// When set to `true` also alarms for related source devices will be included in the request. When this parameter is provided a `source` must be specified. - public func getNumberOfAlarms(dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) throws -> AnyPublisher { + public func getNumberOfAlarms(dateFrom: String? = nil, dateTo: String? = nil, resolved: Bool? = nil, severity: String? = nil, source: String? = nil, status: String? = nil, type: [String]? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } if let parameter = dateTo { queryItems.append(URLQueryItem(name: "dateTo", value: String(parameter))) } @@ -542,16 +502,12 @@ public class AlarmsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: Int.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/ApplicationBinariesApi.swift b/Sources/CumulocityCoreLibrary/Api/ApplicationBinariesApi.swift index b6543ee..3d8cd58 100644 --- a/Sources/CumulocityCoreLibrary/Api/ApplicationBinariesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ApplicationBinariesApi.swift @@ -31,7 +31,7 @@ public class ApplicationBinariesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the application. - public func getApplicationAttachments(id: String) throws -> AnyPublisher { + public func getApplicationAttachments(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/binaries") .set(httpMethod: "get") @@ -40,21 +40,12 @@ public class ApplicationBinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationBinaries.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -86,9 +77,9 @@ public class ApplicationBinariesApi: AdaptableApi { /// The ZIP file to be uploaded. /// - id /// Unique identifier of the application. - public func uploadApplicationAttachment(file: Data, id: String) throws -> AnyPublisher { + public func uploadApplicationAttachment(file: Data, id: String) -> AnyPublisher { let multipartBuilder = MultipartFormDataBuilder() - try multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "application/zip"); + try? multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "application/zip"); let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/binaries") .set(httpMethod: "post") @@ -100,16 +91,12 @@ public class ApplicationBinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -133,7 +120,7 @@ public class ApplicationBinariesApi: AdaptableApi { /// Unique identifier of the application. /// - binaryId /// Unique identifier of the binary. - public func getApplicationAttachment(id: String, binaryId: String) throws -> AnyPublisher { + public func getApplicationAttachment(id: String, binaryId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/binaries/\(binaryId)") .set(httpMethod: "get") @@ -142,16 +129,12 @@ public class ApplicationBinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -177,7 +160,7 @@ public class ApplicationBinariesApi: AdaptableApi { /// Unique identifier of the application. /// - binaryId /// Unique identifier of the binary. - public func deleteApplicationAttachment(id: String, binaryId: String) throws -> AnyPublisher { + public func deleteApplicationAttachment(id: String, binaryId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/binaries/\(binaryId)") .set(httpMethod: "delete") @@ -186,19 +169,12 @@ public class ApplicationBinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift b/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift index 684ff38..ee30981 100644 --- a/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ApplicationsApi.swift @@ -58,7 +58,7 @@ public class ApplicationsApi: 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 getApplications(currentPage: Int? = nil, name: String? = nil, owner: String? = nil, pageSize: Int? = nil, providedFor: String? = nil, subscriber: String? = nil, tenant: String? = nil, type: String? = nil, user: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getApplications(currentPage: Int? = nil, name: String? = nil, owner: String? = nil, pageSize: Int? = nil, providedFor: String? = nil, subscriber: String? = nil, tenant: String? = nil, type: String? = nil, user: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = name { queryItems.append(URLQueryItem(name: "name", value: String(parameter))) } @@ -80,16 +80,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -113,41 +109,35 @@ public class ApplicationsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createApplication(body: C8yApplication) throws -> AnyPublisher { + public func createApplication(body: C8yApplication) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.activeVersionId = nil requestBody.`self` = nil requestBody.id = nil requestBody.resourcesUrl = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/application/applications") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.application+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.application+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 409 else { - let decoder = JSONDecoder() - let error409 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error409) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -170,7 +160,7 @@ public class ApplicationsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the application. - public func getApplication(id: String) throws -> AnyPublisher { + public func getApplication(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)") .set(httpMethod: "get") @@ -179,21 +169,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -217,7 +198,7 @@ public class ApplicationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the application. - public func updateApplication(body: C8yApplication, id: String) throws -> AnyPublisher { + public func updateApplication(body: C8yApplication, id: String) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.activeVersionId = nil @@ -225,31 +206,28 @@ public class ApplicationsApi: AdaptableApi { requestBody.id = nil requestBody.type = nil requestBody.resourcesUrl = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.application+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.application+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -279,7 +257,7 @@ public class ApplicationsApi: AdaptableApi { /// Unique identifier of the application. /// - 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) throws -> AnyPublisher { + public func deleteApplication(id: String, force: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = force { queryItems.append(URLQueryItem(name: "force", value: String(parameter))) } let builder = URLRequestBuilder() @@ -291,24 +269,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -338,7 +304,7 @@ public class ApplicationsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the application. - public func copyApplication(id: String) throws -> AnyPublisher { + public func copyApplication(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/clone") .set(httpMethod: "post") @@ -347,19 +313,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – method not supported") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -380,7 +339,7 @@ public class ApplicationsApi: AdaptableApi { /// - Parameters: /// - name /// The name of the application. - public func getApplicationsByName(name: String) throws -> AnyPublisher { + public func getApplicationsByName(name: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applicationsByName/\(name)") .set(httpMethod: "get") @@ -389,16 +348,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -419,7 +374,7 @@ public class ApplicationsApi: AdaptableApi { /// - Parameters: /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func getApplicationsByTenant(tenantId: String) throws -> AnyPublisher { + public func getApplicationsByTenant(tenantId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applicationsByTenant/\(tenantId)") .set(httpMethod: "get") @@ -428,16 +383,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -466,7 +417,7 @@ public class ApplicationsApi: 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 getApplicationsByOwner(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getApplicationsByOwner(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -481,16 +432,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -519,7 +466,7 @@ public class ApplicationsApi: 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 getApplicationsByUser(username: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getApplicationsByUser(username: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -534,16 +481,12 @@ public class ApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift b/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift index 45e9ec9..b23c447 100644 --- a/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AttachmentsApi.swift @@ -30,7 +30,7 @@ public class AttachmentsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the event. - public func getEventAttachment(id: String) throws -> AnyPublisher { + public func getEventAttachment(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "get") @@ -39,21 +39,12 @@ public class AttachmentsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -78,8 +69,14 @@ public class AttachmentsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the event. - public func replaceEventAttachment(body: Data, id: String) throws -> AnyPublisher { + public func replaceEventAttachment(body: Data, 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: "/event/events/\(id)/binaries") .set(httpMethod: "put") @@ -90,21 +87,12 @@ public class AttachmentsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEventBinary.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -162,8 +150,14 @@ public class AttachmentsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the event. - public func uploadEventAttachment(body: Data, id: String) throws -> AnyPublisher { + public func uploadEventAttachment(body: Data, 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: "/event/events/\(id)/binaries") .set(httpMethod: "post") @@ -174,26 +168,12 @@ public class AttachmentsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - let decoder = JSONDecoder() - let error409 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error409) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEventBinary.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -253,10 +233,10 @@ public class AttachmentsApi: AdaptableApi { /// Path of the file to be uploaded. /// - id /// Unique identifier of the event. - public func uploadEventAttachment(`object`: C8yBinaryInfo, file: Data, id: String) throws -> AnyPublisher { + public func uploadEventAttachment(`object`: C8yBinaryInfo, file: Data, id: String) -> AnyPublisher { let multipartBuilder = MultipartFormDataBuilder() - try multipartBuilder.addBodyPart(named: "object", data: `object`, mimeType: "application/json"); - try multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/plain"); + try? multipartBuilder.addBodyPart(named: "object", data: `object`, mimeType: "application/json"); + try? multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/plain"); let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "post") @@ -268,26 +248,12 @@ public class AttachmentsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - let decoder = JSONDecoder() - let error409 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error409) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEventBinary.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -310,7 +276,7 @@ public class AttachmentsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the event. - public func deleteEventAttachment(id: String) throws -> AnyPublisher { + public func deleteEventAttachment(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)/binaries") .set(httpMethod: "delete") @@ -319,21 +285,12 @@ public class AttachmentsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift b/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift index 27baa21..035beb7 100644 --- a/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/AuditsApi.swift @@ -52,7 +52,7 @@ public class AuditsApi: 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 getAuditRecords(application: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, source: String? = nil, type: String? = nil, user: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getAuditRecords(application: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, source: String? = nil, type: String? = nil, user: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = application { queryItems.append(URLQueryItem(name: "application", value: String(parameter))) } if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } @@ -73,16 +73,12 @@ public class AuditsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAuditRecordCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -102,7 +98,7 @@ public class AuditsApi: AdaptableApi { /// Authentication information is missing or invalid. /// - Parameters: /// - body - public func createAuditRecord(body: C8yAuditRecord) throws -> AnyPublisher { + public func createAuditRecord(body: C8yAuditRecord) -> AnyPublisher { var requestBody = body requestBody.severity = nil requestBody.application = nil @@ -112,26 +108,28 @@ public class AuditsApi: AdaptableApi { requestBody.`self` = nil requestBody.id = nil requestBody.source?.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/audit/auditRecords") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.auditrecord+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.auditrecord+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAuditRecord.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -152,7 +150,7 @@ public class AuditsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the audit record. - public func getAuditRecord(id: String) throws -> AnyPublisher { + public func getAuditRecord(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/audit/auditRecords/\(id)") .set(httpMethod: "get") @@ -161,16 +159,12 @@ public class AuditsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAuditRecord.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift b/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift index b8b941c..2560394 100644 --- a/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/BinariesApi.swift @@ -45,7 +45,7 @@ public class BinariesApi: AdaptableApi { /// The type of managed object to search for. /// - 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 getBinaries(childAdditionId: String? = nil, childAssetId: String? = nil, childDeviceId: String? = nil, currentPage: Int? = nil, ids: [String]? = nil, owner: String? = nil, pageSize: Int? = nil, text: String? = nil, type: String? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getBinaries(childAdditionId: String? = nil, childAssetId: String? = nil, childDeviceId: String? = nil, currentPage: Int? = nil, ids: [String]? = nil, owner: String? = nil, pageSize: Int? = nil, text: String? = nil, type: String? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = childAdditionId { queryItems.append(URLQueryItem(name: "childAdditionId", value: String(parameter))) } if let parameter = childAssetId { queryItems.append(URLQueryItem(name: "childAssetId", value: String(parameter))) } @@ -66,16 +66,12 @@ public class BinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBinaryCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -106,10 +102,10 @@ public class BinariesApi: AdaptableApi { /// - `object` /// - file /// Path of the file to be uploaded. - public func uploadBinary(`object`: C8yBinaryInfo, file: Data) throws -> AnyPublisher { + public func uploadBinary(`object`: C8yBinaryInfo, file: Data) -> AnyPublisher { let multipartBuilder = MultipartFormDataBuilder() - try multipartBuilder.addBodyPart(named: "object", data: `object`, mimeType: "application/json"); - try multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/plain"); + try? multipartBuilder.addBodyPart(named: "object", data: `object`, mimeType: "application/json"); + try? multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/plain"); let builder = URLRequestBuilder() .set(resourcePath: "/inventory/binaries") .set(httpMethod: "post") @@ -121,22 +117,12 @@ public class BinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 400 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBinary.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -157,7 +143,7 @@ public class BinariesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getBinary(id: String) throws -> AnyPublisher { + public func getBinary(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/binaries/\(id)") .set(httpMethod: "get") @@ -166,16 +152,12 @@ public class BinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -197,8 +179,14 @@ public class BinariesApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func replaceBinary(body: Data, id: String) throws -> AnyPublisher { + public func replaceBinary(body: Data, 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: "/inventory/binaries/\(id)") .set(httpMethod: "put") @@ -209,16 +197,12 @@ public class BinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBinary.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -239,7 +223,7 @@ public class BinariesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func removeBinary(id: String) throws -> AnyPublisher { + public func removeBinary(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/binaries/\(id)") .set(httpMethod: "delete") @@ -248,16 +232,12 @@ public class BinariesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/BootstrapUserApi.swift b/Sources/CumulocityCoreLibrary/Api/BootstrapUserApi.swift index fe7f6d2..7ce8509 100644 --- a/Sources/CumulocityCoreLibrary/Api/BootstrapUserApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/BootstrapUserApi.swift @@ -32,7 +32,7 @@ public class BootstrapUserApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the application. - public func getBootstrapUser(id: String) throws -> AnyPublisher { + public func getBootstrapUser(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/applications/\(id)/bootstrapUser") .set(httpMethod: "get") @@ -41,21 +41,12 @@ public class BootstrapUserApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 400 else { - let decoder = JSONDecoder() - let error400 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error400) - } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBootstrapUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/BulkOperationsApi.swift b/Sources/CumulocityCoreLibrary/Api/BulkOperationsApi.swift index 3463fc8..981b75a 100644 --- a/Sources/CumulocityCoreLibrary/Api/BulkOperationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/BulkOperationsApi.swift @@ -48,7 +48,7 @@ public class BulkOperationsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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)). - public func getBulkOperations(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) throws -> AnyPublisher { + public func getBulkOperations(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -62,16 +62,12 @@ public class BulkOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBulkOperationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -91,7 +87,7 @@ public class BulkOperationsApi: AdaptableApi { /// Authentication information is missing or invalid. /// - Parameters: /// - body - public func createBulkOperation(body: C8yBulkOperation) throws -> AnyPublisher { + public func createBulkOperation(body: C8yBulkOperation) -> AnyPublisher { var requestBody = body requestBody.generalStatus = nil requestBody.failedParentId = nil @@ -99,26 +95,28 @@ public class BulkOperationsApi: AdaptableApi { requestBody.progress = nil requestBody.id = nil requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/bulkoperations") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.bulkoperation+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.bulkoperation+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBulkOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -141,7 +139,7 @@ public class BulkOperationsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the bulk operation. - public func getBulkOperation(id: String) throws -> AnyPublisher { + public func getBulkOperation(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/bulkoperations/\(id)") .set(httpMethod: "get") @@ -150,21 +148,12 @@ public class BulkOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBulkOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -188,7 +177,7 @@ public class BulkOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the bulk operation. - public func updateBulkOperation(body: C8yBulkOperation, id: String) throws -> AnyPublisher { + public func updateBulkOperation(body: C8yBulkOperation, id: String) -> AnyPublisher { var requestBody = body requestBody.generalStatus = nil requestBody.failedParentId = nil @@ -196,31 +185,28 @@ public class BulkOperationsApi: AdaptableApi { requestBody.progress = nil requestBody.id = nil requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/bulkoperations/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.bulkoperation+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.bulkoperation+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBulkOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -245,7 +231,7 @@ public class BulkOperationsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the bulk operation. - public func deleteBulkOperation(id: String) throws -> AnyPublisher { + public func deleteBulkOperation(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/bulkoperations/\(id)") .set(httpMethod: "delete") @@ -254,24 +240,12 @@ public class BulkOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift b/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift index c368cc4..d8b3cf4 100644 --- a/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ChildOperationsApi.swift @@ -49,7 +49,7 @@ public class ChildOperationsApi: 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 getChildAdditions(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getChildAdditions(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -67,26 +67,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -114,33 +100,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAddition(body: C8yChildOperationsAddOne, id: String) throws -> AnyPublisher { + public func assignAsChildAddition(body: C8yChildOperationsAddOne, 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: "/inventory/managedObjects/\(id)/childAdditions") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreference+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -168,33 +151,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAddition(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func assignAsChildAddition(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childAdditions") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -222,7 +202,7 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAddition(body: C8yManagedObject, id: String) throws -> AnyPublisher { + public func assignAsChildAddition(body: C8yManagedObject, id: String) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.additionParents = nil @@ -235,31 +215,28 @@ public class ChildOperationsApi: AdaptableApi { requestBody.assetParents = nil requestBody.deviceParents = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAdditions") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobject+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -285,36 +262,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func unassignChildAdditions(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func unassignChildAdditions(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childAdditions") .set(httpMethod: "delete") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -341,7 +312,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func getChildAddition(id: String, childId: String) throws -> AnyPublisher { + public func getChildAddition(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAdditions/\(childId)") .set(httpMethod: "get") @@ -350,26 +321,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -396,7 +353,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func unassignChildAddition(id: String, childId: String) throws -> AnyPublisher { + public func unassignChildAddition(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAdditions/\(childId)") .set(httpMethod: "delete") @@ -405,26 +362,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -463,7 +406,7 @@ public class ChildOperationsApi: 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 getChildAssets(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getChildAssets(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -481,26 +424,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -528,33 +457,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAsset(body: C8yChildOperationsAddOne, id: String) throws -> AnyPublisher { + public func assignAsChildAsset(body: C8yChildOperationsAddOne, 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: "/inventory/managedObjects/\(id)/childAssets") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreference+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -582,33 +508,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAsset(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func assignAsChildAsset(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childAssets") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -636,7 +559,7 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildAsset(body: C8yManagedObject, id: String) throws -> AnyPublisher { + public func assignAsChildAsset(body: C8yManagedObject, id: String) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.additionParents = nil @@ -649,31 +572,28 @@ public class ChildOperationsApi: AdaptableApi { requestBody.assetParents = nil requestBody.deviceParents = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAssets") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobject+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -699,36 +619,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func unassignChildAssets(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func unassignChildAssets(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childAssets") .set(httpMethod: "delete") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -755,7 +669,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func getChildAsset(id: String, childId: String) throws -> AnyPublisher { + public func getChildAsset(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAssets/\(childId)") .set(httpMethod: "get") @@ -764,26 +678,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -810,7 +710,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func unassignChildAsset(id: String, childId: String) throws -> AnyPublisher { + public func unassignChildAsset(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childAssets/\(childId)") .set(httpMethod: "delete") @@ -819,26 +719,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -877,7 +763,7 @@ public class ChildOperationsApi: 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 getChildDevices(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getChildDevices(id: String, currentPage: Int? = nil, pageSize: Int? = nil, query: String? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -895,26 +781,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -942,33 +814,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildDevice(body: C8yChildOperationsAddOne, id: String) throws -> AnyPublisher { + public func assignAsChildDevice(body: C8yChildOperationsAddOne, 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: "/inventory/managedObjects/\(id)/childDevices") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreference+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -996,33 +865,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildDevice(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func assignAsChildDevice(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childDevices") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -1050,7 +916,7 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func assignAsChildDevice(body: C8yManagedObject, id: String) throws -> AnyPublisher { + public func assignAsChildDevice(body: C8yManagedObject, id: String) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.additionParents = nil @@ -1063,31 +929,28 @@ public class ChildOperationsApi: AdaptableApi { requestBody.assetParents = nil requestBody.deviceParents = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childDevices") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobject+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -1113,36 +976,30 @@ public class ChildOperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func unassignChildDevices(body: C8yChildOperationsAddMultiple, id: String) throws -> AnyPublisher { + public func unassignChildDevices(body: C8yChildOperationsAddMultiple, 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: "/inventory/managedObjects/\(id)/childDevices") .set(httpMethod: "delete") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectreferencecollection+json") .add(header: "Accept", value: "application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -1169,7 +1026,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func getChildDevice(id: String, childId: String) throws -> AnyPublisher { + public func getChildDevice(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childDevices/\(childId)") .set(httpMethod: "get") @@ -1178,26 +1035,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -1224,7 +1067,7 @@ public class ChildOperationsApi: AdaptableApi { /// Unique identifier of the managed object. /// - childId /// Unique identifier of the child object. - public func unassignChildDevice(id: String, childId: String) throws -> AnyPublisher { + public func unassignChildDevice(id: String, childId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/childDevices/\(childId)") .set(httpMethod: "delete") @@ -1233,26 +1076,12 @@ public class ChildOperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/CurrentApplicationApi.swift b/Sources/CumulocityCoreLibrary/Api/CurrentApplicationApi.swift index 7e0cc23..a0a6ff6 100644 --- a/Sources/CumulocityCoreLibrary/Api/CurrentApplicationApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/CurrentApplicationApi.swift @@ -30,7 +30,7 @@ public class CurrentApplicationApi: AdaptableApi { /// Authentication information is missing or invalid. /// - 403 /// Not enough permissions/roles to perform this operation. - public func getCurrentApplication() throws -> AnyPublisher { + public func getCurrentApplication() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/currentApplication") .set(httpMethod: "get") @@ -39,21 +39,12 @@ public class CurrentApplicationApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -77,38 +68,35 @@ public class CurrentApplicationApi: AdaptableApi { /// - Parameters: /// - body @available(*, deprecated) - public func updateCurrentApplication(body: C8yApplication) throws -> AnyPublisher { + public func updateCurrentApplication(body: C8yApplication) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.activeVersionId = nil requestBody.`self` = nil requestBody.id = nil requestBody.resourcesUrl = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/application/currentApplication") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.application+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.application+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplication.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -129,7 +117,7 @@ public class CurrentApplicationApi: AdaptableApi { /// Authentication information is missing or invalid. /// - 403 /// Not enough permissions/roles to perform this operation. - public func getCurrentApplicationSettings() throws -> AnyPublisher<[C8yApplicationSettings], Swift.Error> { + public func getCurrentApplicationSettings() -> AnyPublisher<[C8yApplicationSettings], Error> { let builder = URLRequestBuilder() .set(resourcePath: "/application/currentApplication/settings") .set(httpMethod: "get") @@ -138,21 +126,12 @@ public class CurrentApplicationApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: [C8yApplicationSettings].self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -170,7 +149,7 @@ public class CurrentApplicationApi: AdaptableApi { /// The request has succeeded and the list of subscribed users for the current application is sent in the response. /// - 401 /// Authentication information is missing or invalid. - public func getSubscribedUsers() throws -> AnyPublisher { + public func getSubscribedUsers() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/application/currentApplication/subscriptions") .set(httpMethod: "get") @@ -179,16 +158,12 @@ public class CurrentApplicationApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationUserCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift b/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift index aef7263..9ba7a67 100644 --- a/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/CurrentUserApi.swift @@ -28,7 +28,7 @@ public class CurrentUserApi: AdaptableApi { /// The request has succeeded and the current user is sent in the response. /// - 401 /// Authentication information is missing or invalid. - public func getCurrentUser() throws -> AnyPublisher { + public func getCurrentUser() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/currentUser") .set(httpMethod: "get") @@ -37,16 +37,12 @@ public class CurrentUserApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yCurrentUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -68,38 +64,251 @@ public class CurrentUserApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func updateCurrentUser(body: C8yCurrentUser) throws -> AnyPublisher { + public func updateCurrentUser(body: C8yCurrentUser) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.effectiveRoles = nil requestBody.shouldResetPassword = nil requestBody.id = nil requestBody.lastPasswordChange = nil + requestBody.twoFactorAuthenticationEnabled = nil requestBody.devicePermissions = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/currentUser") .set(httpMethod: "put") .add(header: "Content-Type", value: "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") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") + return element.data + }).decode(type: C8yCurrentUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Update the current user's password + /// Update the current user's password. + /// + /// > **⚠️ Important:** If the tenant uses OAI-Secure authentication, the current user will not be logged out. Instead, a new cookie will be set with a new token, and the previous token will expire within a minute. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_OWN_ADMIN + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The current user password was updated. + /// - 401 + /// Authentication information is missing or invalid. + /// - 422 + /// Unprocessable Entity – invalid payload. + /// - Parameters: + /// - body + public func updateCurrentUserPassword(body: C8yPasswordChange) -> 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/currentUser/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) } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data - }).decode(type: C8yCurrentUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() + }).eraseToAnyPublisher() + } + + /// Generate secret to set up TFA + /// 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*. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_OWN_READ OR ROLE_SYSTEM + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The request has succeeded and the secret is sent in the response. + /// - 401 + /// Authentication information is missing or invalid. + public func generateTfaSecret() -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/user/currentUser/totpSecret") + .set(httpMethod: "post") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).decode(type: C8yCurrentUserTotpSecret.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Returns the activation state of the two-factor authentication feature. + /// Returns the activation state of the two-factor authentication feature for the current user. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_OWN_READ OR ROLE_SYSTEM + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// Returns the activation state. + /// - 401 + /// Authentication information is missing or invalid. + /// - 404 + /// User not found. + public func getTfaState() -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/user/currentUser/totpSecret/activity") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).decode(type: C8yCurrentUserTotpSecretActivity.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Activates or deactivates the two-factor authentication feature + /// 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*. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_OWN_READ OR ROLE_SYSTEM + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 204 + /// The two-factor authentication was activated or deactivated. + /// - 401 + /// Authentication information is missing or invalid. + /// - 403 + /// Cannot deactivate TOTP setup. + /// - 404 + /// User not found. + /// - Parameters: + /// - body + public func setTfaState(body: C8yCurrentUserTotpSecretActivity) -> 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/currentUser/totpSecret/activity") + .set(httpMethod: "post") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).eraseToAnyPublisher() + } + + /// Verify TFA code + /// Verifies the authentication code that the current user received from a TFA app/service and uploaded to the platform to gain access or enable the two-factor authentication feature. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_OWN_READ OR ROLE_SYSTEM + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 204 + /// The sent code was correct and the access can be granted. + /// - 401 + /// Authentication information is missing or invalid. + /// - 403 + /// Invalid verification code. + /// - 404 + /// Cannot validate TFA TOTP code - user's TFA TOTP secret does not exist. + /// - 422 + /// Unprocessable Entity – invalid payload. + /// - Parameters: + /// - body + public func verifyTfaCode(body: C8yCurrentUserTotpCode) -> 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/currentUser/totpSecret/verify") + .set(httpMethod: "post") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).eraseToAnyPublisher() } } diff --git a/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift b/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift index b13e704..79f8901 100644 --- a/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/DeviceCredentialsApi.swift @@ -36,32 +36,34 @@ public class DeviceCredentialsApi: AdaptableApi { /// Authentication information is missing or invalid. /// - Parameters: /// - body - public func createDeviceCredentials(body: C8yDeviceCredentials) throws -> AnyPublisher { + public func createDeviceCredentials(body: C8yDeviceCredentials) -> AnyPublisher { var requestBody = body requestBody.password = nil requestBody.tenantId = nil requestBody.`self` = nil requestBody.username = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/deviceCredentials") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.devicecredentials+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.devicecredentials+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yDeviceCredentials.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -141,9 +143,9 @@ public class DeviceCredentialsApi: AdaptableApi { /// - Parameters: /// - file /// The CSV file to be uploaded. - public func createBulkDeviceCredentials(file: Data) throws -> AnyPublisher { + public func createBulkDeviceCredentials(file: Data) -> AnyPublisher { let multipartBuilder = MultipartFormDataBuilder() - try multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/csv"); + try? multipartBuilder.addBodyPart(named: "file", data: file, mimeType: "text/csv"); let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/bulkNewDeviceRequests") .set(httpMethod: "post") @@ -155,16 +157,12 @@ public class DeviceCredentialsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yBulkNewDeviceRequest.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift b/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift index 8b3c6af..9540c1c 100644 --- a/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/DeviceStatisticsApi.swift @@ -74,7 +74,7 @@ public class DeviceStatisticsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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 getMonthlyDeviceStatistics(tenantId: String, date: Date, currentPage: Int? = nil, deviceId: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getMonthlyDeviceStatistics(tenantId: String, date: Date, currentPage: Int? = nil, deviceId: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = deviceId { queryItems.append(URLQueryItem(name: "deviceId", value: String(parameter))) } @@ -89,19 +89,12 @@ public class DeviceStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yDeviceStatisticsCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -134,7 +127,7 @@ public class DeviceStatisticsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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 getDailyDeviceStatistics(tenantId: String, date: Date, currentPage: Int? = nil, deviceId: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getDailyDeviceStatistics(tenantId: String, date: Date, currentPage: Int? = nil, deviceId: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = deviceId { queryItems.append(URLQueryItem(name: "deviceId", value: String(parameter))) } @@ -149,19 +142,12 @@ public class DeviceStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yDeviceStatisticsCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/EventsApi.swift b/Sources/CumulocityCoreLibrary/Api/EventsApi.swift index 210378a..7211c34 100644 --- a/Sources/CumulocityCoreLibrary/Api/EventsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/EventsApi.swift @@ -18,7 +18,7 @@ public class EventsApi: AdaptableApi { /// Retrieve all events /// Retrieve all events on your tenant. /// - /// In case of executing [range queries](https://en.wikipedia.org/wiki/Range_query_(database)) between an upper and lower boundary, for example, querying using `dateFrom`–`dateTo` or `createdFrom`–`createdTo`, the oldest registered events are returned first. It is possible to change the order using the query parameter `revert=true`. + /// In case of executing [range queries](https://en.wikipedia.org/wiki/Range_query_(database)) between an upper and lower boundary, for example, querying using `dateFrom`–`dateTo` or `createdFrom`–`createdTo`, the newest registered events are returned first. It is possible to change the order using the query parameter `revert=true`. /// ///
Required roles
/// ROLE_EVENT_READ @@ -52,7 +52,7 @@ public class EventsApi: AdaptableApi { /// - pageSize /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - revert - /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the oldest events first. By default, the results are sorted by the latest events first. + /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the oldest events first. By default, the results are sorted by the newest events first. /// - source /// The managed object ID to which the event is associated. /// - type @@ -65,7 +65,7 @@ public class EventsApi: 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 getEvents(createdFrom: String? = nil, createdTo: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, fragmentValue: String? = nil, lastUpdatedFrom: String? = nil, lastUpdatedTo: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, source: String? = nil, type: String? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getEvents(createdFrom: String? = nil, createdTo: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, fragmentValue: String? = nil, lastUpdatedFrom: String? = nil, lastUpdatedTo: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, source: String? = nil, type: String? = nil, withSourceAssets: Bool? = nil, withSourceDevices: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = createdFrom { queryItems.append(URLQueryItem(name: "createdFrom", value: String(parameter))) } if let parameter = createdTo { queryItems.append(URLQueryItem(name: "createdTo", value: String(parameter))) } @@ -93,16 +93,12 @@ public class EventsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEventCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -132,39 +128,35 @@ public class EventsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createEvent(body: C8yEvent) throws -> AnyPublisher { + public func createEvent(body: C8yEvent) -> AnyPublisher { var requestBody = body requestBody.lastUpdated = nil requestBody.creationTime = nil requestBody.`self` = nil requestBody.id = nil requestBody.source?.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/event/events") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.event+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.event+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEvent.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -203,7 +195,7 @@ public class EventsApi: AdaptableApi { /// The managed object ID to which the event is associated. /// - type /// The type of event to search for. - public func deleteEvents(createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, source: String? = nil, type: String? = nil) throws -> AnyPublisher { + public func deleteEvents(createdFrom: String? = nil, createdTo: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, source: String? = nil, type: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = createdFrom { queryItems.append(URLQueryItem(name: "createdFrom", value: String(parameter))) } if let parameter = createdTo { queryItems.append(URLQueryItem(name: "createdTo", value: String(parameter))) } @@ -221,19 +213,12 @@ public class EventsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -256,7 +241,7 @@ public class EventsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the event. - public func getEvent(id: String) throws -> AnyPublisher { + public func getEvent(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)") .set(httpMethod: "get") @@ -265,21 +250,12 @@ public class EventsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEvent.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -305,7 +281,7 @@ public class EventsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the event. - public func updateEvent(body: C8yEvent, id: String) throws -> AnyPublisher { + public func updateEvent(body: C8yEvent, id: String) -> AnyPublisher { var requestBody = body requestBody.lastUpdated = nil requestBody.creationTime = nil @@ -314,34 +290,28 @@ public class EventsApi: AdaptableApi { requestBody.source = nil requestBody.time = nil requestBody.type = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.event+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.event+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yEvent.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -366,7 +336,7 @@ public class EventsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the event. - public func deleteEvent(id: String) throws -> AnyPublisher { + public func deleteEvent(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/event/events/\(id)") .set(httpMethod: "delete") @@ -375,24 +345,12 @@ public class EventsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift b/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift index c5ba60c..a2ce8cf 100644 --- a/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ExternalIDsApi.swift @@ -31,7 +31,7 @@ public class ExternalIDsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getExternalIds(id: String) throws -> AnyPublisher { + public func getExternalIds(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/identity/globalIds/\(id)/externalIds") .set(httpMethod: "get") @@ -40,16 +40,12 @@ public class ExternalIDsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yExternalIds.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -73,33 +69,32 @@ public class ExternalIDsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func createExternalId(body: C8yExternalId, id: String) throws -> AnyPublisher { + public func createExternalId(body: C8yExternalId, id: String) -> AnyPublisher { var requestBody = body requestBody.managedObject = nil requestBody.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/identity/globalIds/\(id)/externalIds") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.externalid+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.externalid+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicate – Identity already bound to a different Global ID.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yExternalId.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -124,7 +119,7 @@ public class ExternalIDsApi: AdaptableApi { /// The identifier used in the external system that Cumulocity IoT interfaces with. /// - externalId /// The type of the external identifier. - public func getExternalId(type: String, externalId: String) throws -> AnyPublisher { + public func getExternalId(type: String, externalId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/identity/externalIds/\(type)/\(externalId)") .set(httpMethod: "get") @@ -133,21 +128,12 @@ public class ExternalIDsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yExternalId.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -172,7 +158,7 @@ public class ExternalIDsApi: AdaptableApi { /// The identifier used in the external system that Cumulocity IoT interfaces with. /// - externalId /// The type of the external identifier. - public func deleteExternalId(type: String, externalId: String) throws -> AnyPublisher { + public func deleteExternalId(type: String, externalId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/identity/externalIds/\(type)/\(externalId)") .set(httpMethod: "delete") @@ -181,21 +167,12 @@ public class ExternalIDsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/GroupsApi.swift b/Sources/CumulocityCoreLibrary/Api/GroupsApi.swift index ff7826f..fc2cd7f 100644 --- a/Sources/CumulocityCoreLibrary/Api/GroupsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/GroupsApi.swift @@ -43,7 +43,7 @@ public class GroupsApi: 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 getUserGroups(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getTenantUserGroups(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -58,21 +58,12 @@ public class GroupsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUserGroupCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -100,7 +91,7 @@ public class GroupsApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func createUserGroup(body: C8yGroup, tenantId: String) throws -> AnyPublisher { + public func createUserGroup(body: C8yGroup, tenantId: String) -> AnyPublisher { var requestBody = body requestBody.roles = nil requestBody.`self` = nil @@ -108,37 +99,28 @@ public class GroupsApi: AdaptableApi { requestBody.devicePermissions = nil requestBody.users = nil requestBody.applications = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.group+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.group+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicate – Group name already exists.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yGroup.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -165,7 +147,7 @@ public class GroupsApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupId /// Unique identifier of the user group. - public func getUserGroup(tenantId: String, groupId: Int) throws -> AnyPublisher { + public func getUserGroup(tenantId: String, groupId: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups/\(groupId)") .set(httpMethod: "get") @@ -174,26 +156,12 @@ public class GroupsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yGroup.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -223,7 +191,7 @@ public class GroupsApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupId /// Unique identifier of the user group. - public func updateUserGroup(body: C8yGroup, tenantId: String, groupId: Int) throws -> AnyPublisher { + public func updateUserGroup(body: C8yGroup, tenantId: String, groupId: Int) -> AnyPublisher { var requestBody = body requestBody.roles = nil requestBody.`self` = nil @@ -231,39 +199,28 @@ public class GroupsApi: AdaptableApi { requestBody.devicePermissions = nil requestBody.users = nil requestBody.applications = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups/\(groupId)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.group+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.group+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yGroup.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -290,7 +247,7 @@ public class GroupsApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupId /// Unique identifier of the user group. - public func deleteUserGroup(tenantId: String, groupId: Int) throws -> AnyPublisher { + public func deleteUserGroup(tenantId: String, groupId: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups/\(groupId)") .set(httpMethod: "delete") @@ -299,24 +256,12 @@ public class GroupsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -343,7 +288,7 @@ public class GroupsApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupName /// The name of the user group. - public func getUserGroupByName(tenantId: String, groupName: String) throws -> AnyPublisher { + public func getUserGroupByName(tenantId: String, groupName: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groupByName/\(groupName)") .set(httpMethod: "get") @@ -352,26 +297,12 @@ public class GroupsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yGroup.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -406,7 +337,7 @@ public class GroupsApi: 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 getUserGroups(tenantId: String, userId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getUserGroups(tenantId: String, userId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -421,26 +352,12 @@ public class GroupsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yGroupReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/IdentityApi.swift b/Sources/CumulocityCoreLibrary/Api/IdentityApi.swift index 8f5a1ba..cca8065 100644 --- a/Sources/CumulocityCoreLibrary/Api/IdentityApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/IdentityApi.swift @@ -30,7 +30,7 @@ public class IdentityApi: AdaptableApi { /// The request has succeeded and the URIs are sent in the response. /// - 401 /// Authentication information is missing or invalid. - public func getIdentityApiResource() throws -> AnyPublisher { + public func getIdentityApiResource() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/identity") .set(httpMethod: "get") @@ -39,16 +39,12 @@ public class IdentityApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yIdentityApiResource.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/InventoryApi.swift b/Sources/CumulocityCoreLibrary/Api/InventoryApi.swift index b279812..9b165da 100644 --- a/Sources/CumulocityCoreLibrary/Api/InventoryApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/InventoryApi.swift @@ -27,7 +27,7 @@ public class InventoryApi: AdaptableApi { /// Authentication information is missing or invalid. /// - 403 /// Not authorized to perform this operation. - public func getInventoryApiResource() throws -> AnyPublisher { + public func getInventoryApiResource() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory") .set(httpMethod: "get") @@ -38,7 +38,7 @@ public class InventoryApi: AdaptableApi { } guard httpResponse.statusCode != 401 else { let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) + let error401 = try! decoder.decode(C8yError.self, from: element.data) throw Errors.badResponseError(response: httpResponse, reason: error401) } guard httpResponse.statusCode != 403 else { diff --git a/Sources/CumulocityCoreLibrary/Api/InventoryRolesApi.swift b/Sources/CumulocityCoreLibrary/Api/InventoryRolesApi.swift index f0ae157..3581089 100644 --- a/Sources/CumulocityCoreLibrary/Api/InventoryRolesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/InventoryRolesApi.swift @@ -35,7 +35,7 @@ public class InventoryRolesApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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)). - public func getInventoryRoles(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) throws -> AnyPublisher { + public func getInventoryRoles(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -49,16 +49,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryRoleCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -80,33 +76,32 @@ public class InventoryRolesApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createInventoryRole(body: C8yInventoryRole) throws -> AnyPublisher { + public func createInventoryRole(body: C8yInventoryRole) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/inventoryroles") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.inventoryrole+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.inventoryrole+json, application/vnd.com.nsn.cumulocity.error+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryRole.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -129,7 +124,7 @@ public class InventoryRolesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the inventory role. - public func getInventoryRole(id: Int) throws -> AnyPublisher { + public func getInventoryRole(id: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/inventoryroles/\(id)") .set(httpMethod: "get") @@ -138,21 +133,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryRole.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -178,38 +164,32 @@ public class InventoryRolesApi: AdaptableApi { /// - body /// - id /// Unique identifier of the inventory role. - public func updateInventoryRole(body: C8yInventoryRole, id: Int) throws -> AnyPublisher { + public func updateInventoryRole(body: C8yInventoryRole, id: Int) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/inventoryroles/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.inventoryrole+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.inventoryrole+json, application/vnd.com.nsn.cumulocity.error+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryRole.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -234,7 +214,7 @@ public class InventoryRolesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the inventory role. - public func deleteInventoryRole(id: Int) throws -> AnyPublisher { + public func deleteInventoryRole(id: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/inventoryroles/\(id)") .set(httpMethod: "delete") @@ -243,24 +223,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -287,7 +255,7 @@ public class InventoryRolesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func getUserInventoryRoles(tenantId: String, userId: String) throws -> AnyPublisher { + public func getUserInventoryRoles(tenantId: String, userId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)/roles/inventory") .set(httpMethod: "get") @@ -296,26 +264,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryAssignmentCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -345,43 +299,32 @@ public class InventoryRolesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func assignUserInventoryRole(body: C8yInventoryAssignment, tenantId: String, userId: String) throws -> AnyPublisher { + public func assignUserInventoryRole(body: C8yInventoryAssignment, tenantId: String, userId: String) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + 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)/roles/inventory") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.inventoryassignment+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.inventoryassignment+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryAssignment.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -410,7 +353,7 @@ public class InventoryRolesApi: AdaptableApi { /// Unique identifier of the a user. /// - id /// Unique identifier of the inventory assignment. - public func getUserInventoryRole(tenantId: String, userId: String, id: Int) throws -> AnyPublisher { + public func getUserInventoryRole(tenantId: String, userId: String, id: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)/roles/inventory/\(id)") .set(httpMethod: "get") @@ -419,26 +362,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryAssignment.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -470,41 +399,30 @@ public class InventoryRolesApi: AdaptableApi { /// Unique identifier of the a user. /// - id /// Unique identifier of the inventory assignment. - public func updateUserInventoryRole(body: C8yInventoryAssignmentReference, tenantId: String, userId: String, id: Int) throws -> AnyPublisher { + public func updateUserInventoryRole(body: C8yInventoryAssignmentReference, tenantId: String, userId: String, id: Int) -> 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)/roles/inventory/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.inventoryassignment+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.inventoryassignment+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yInventoryAssignment.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -533,7 +451,7 @@ public class InventoryRolesApi: AdaptableApi { /// Unique identifier of the a user. /// - id /// Unique identifier of the inventory assignment. - public func unassignUserInventoryRole(tenantId: String, userId: String, id: Int) throws -> AnyPublisher { + public func unassignUserInventoryRole(tenantId: String, userId: String, id: Int) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)/roles/inventory/\(id)") .set(httpMethod: "delete") @@ -542,24 +460,12 @@ public class InventoryRolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift index b4fac96..e85b3c8 100644 --- a/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/LoginOptionsApi.swift @@ -29,7 +29,7 @@ public class LoginOptionsApi: AdaptableApi { /// If this is set to `true`, the management tenant login options will be returned. > **ⓘ Info:** The `tenantId` parameter must not be present in the request when using the `management` parameter, otherwise it will cause an error. /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func getLoginOptions(management: Bool? = nil, tenantId: String? = nil) throws -> AnyPublisher { + public func getLoginOptions(management: Bool? = nil, tenantId: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = management { queryItems.append(URLQueryItem(name: "management", value: String(parameter))) } if let parameter = tenantId { queryItems.append(URLQueryItem(name: "tenantId", value: String(parameter))) } @@ -42,16 +42,12 @@ public class LoginOptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 400 else { - let decoder = JSONDecoder() - let error400 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error400) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yLoginOptionCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -75,35 +71,31 @@ public class LoginOptionsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createLoginOption(body: C8yAuthConfig) throws -> AnyPublisher { + public func createLoginOption(body: C8yAuthConfig) -> 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") .set(httpMethod: "post") .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: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 400 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicated – The login option already exists.") - } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yAuthConfig.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift b/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift index 04cde23..c8d28af 100644 --- a/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/ManagedObjectsApi.swift @@ -67,7 +67,7 @@ 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) throws -> AnyPublisher { + 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 { var queryItems: [URLQueryItem] = [] if let parameter = childAdditionId { queryItems.append(URLQueryItem(name: "childAdditionId", value: String(parameter))) } if let parameter = childAssetId { queryItems.append(URLQueryItem(name: "childAssetId", value: String(parameter))) } @@ -98,21 +98,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -146,7 +137,7 @@ public class ManagedObjectsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createManagedObject(body: C8yManagedObject) throws -> AnyPublisher { + public func createManagedObject(body: C8yManagedObject) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.additionParents = nil @@ -159,29 +150,28 @@ public class ManagedObjectsApi: AdaptableApi { requestBody.assetParents = nil requestBody.deviceParents = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobject+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.managedobject+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObject.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -216,7 +206,7 @@ public class ManagedObjectsApi: AdaptableApi { /// 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) throws -> AnyPublisher { + 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 { var queryItems: [URLQueryItem] = [] if let parameter = childAdditionId { queryItems.append(URLQueryItem(name: "childAdditionId", value: String(parameter))) } if let parameter = childAssetId { queryItems.append(URLQueryItem(name: "childAssetId", value: String(parameter))) } @@ -235,16 +225,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: Int.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -275,7 +261,7 @@ 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) throws -> AnyPublisher { + public func getManagedObject(id: String, skipChildrenNames: Bool? = nil, withChildren: Bool? = nil, withChildrenCount: Bool? = nil, withParents: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = skipChildrenNames { queryItems.append(URLQueryItem(name: "skipChildrenNames", value: String(parameter))) } if let parameter = withChildren { queryItems.append(URLQueryItem(name: "withChildren", value: String(parameter))) } @@ -290,21 +276,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObject.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -330,7 +307,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func updateManagedObject(body: C8yManagedObject, id: String) throws -> AnyPublisher { + public func updateManagedObject(body: C8yManagedObject, id: String) -> AnyPublisher { var requestBody = body requestBody.owner = nil requestBody.additionParents = nil @@ -343,31 +320,28 @@ public class ManagedObjectsApi: AdaptableApi { requestBody.assetParents = nil requestBody.deviceParents = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobject+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.managedobject+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObject.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -398,7 +372,7 @@ public class ManagedObjectsApi: AdaptableApi { /// When set to `true` all the hierarchy will be deleted without checking the type of managed object. It takes precedence over the parameter `cascade`. /// - withDeviceUser /// When set to `true` and the managed object is a device, it deletes the associated device user (credentials). - public func deleteManagedObject(id: String, cascade: Bool? = nil, forceCascade: Bool? = nil, withDeviceUser: Bool? = nil) throws -> AnyPublisher { + public func deleteManagedObject(id: String, cascade: Bool? = nil, forceCascade: Bool? = nil, withDeviceUser: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = cascade { queryItems.append(URLQueryItem(name: "cascade", value: String(parameter))) } if let parameter = forceCascade { queryItems.append(URLQueryItem(name: "forceCascade", value: String(parameter))) } @@ -412,21 +386,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -449,7 +414,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getLatestAvailability(id: String) throws -> AnyPublisher { + public func getLatestAvailability(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/availability") .set(httpMethod: "get") @@ -458,21 +423,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: String.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -495,7 +451,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getSupportedMeasurements(id: String) throws -> AnyPublisher { + public func getSupportedMeasurements(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/supportedMeasurements") .set(httpMethod: "get") @@ -504,21 +460,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8ySupportedMeasurements.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -541,7 +488,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getSupportedSeries(id: String) throws -> AnyPublisher { + public func getSupportedSeries(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/supportedSeries") .set(httpMethod: "get") @@ -550,21 +497,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8ySupportedSeries.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -587,7 +525,7 @@ public class ManagedObjectsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the managed object. - public func getManagedObjectUser(id: String) throws -> AnyPublisher { + public func getManagedObjectUser(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/user") .set(httpMethod: "get") @@ -596,21 +534,12 @@ public class ManagedObjectsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -634,35 +563,32 @@ public class ManagedObjectsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the managed object. - public func updateManagedObjectUser(body: C8yManagedObjectUser, id: String) throws -> AnyPublisher { + public func updateManagedObjectUser(body: C8yManagedObjectUser, id: String) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.userName = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/inventory/managedObjects/\(id)/user") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.managedobjectuser+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.managedobjectuser+json, application/vnd.com.nsn.cumulocity.error+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yManagedObjectUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift b/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift index f1d571f..c4e2cff 100644 --- a/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/MeasurementsApi.swift @@ -44,7 +44,7 @@ public class MeasurementsApi: AdaptableApi { /// - pageSize /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - revert - /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the latest measurements first. By default, the results are sorted by the oldest measurements first. + /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the newest measurements first. By default, the results are sorted by the oldest measurements first. /// - source /// The managed object ID to which the measurement is associated. /// - type @@ -57,7 +57,7 @@ public class MeasurementsApi: 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 getMeasurements(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, source: String? = nil, type: String? = nil, valueFragmentSeries: String? = nil, valueFragmentType: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getMeasurements(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, source: String? = nil, type: String? = nil, valueFragmentSeries: String? = nil, valueFragmentType: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } @@ -79,16 +79,12 @@ public class MeasurementsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yMeasurementCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -129,37 +125,33 @@ public class MeasurementsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createMeasurement(body: C8yMeasurement) throws -> AnyPublisher { + public func createMeasurement(body: C8yMeasurement) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil requestBody.source?.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/measurement/measurements") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.measurement+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.measurement+json, application/vnd.com.nsn.cumulocity.measurementcollection+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yMeasurement.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -200,38 +192,34 @@ public class MeasurementsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createMeasurement(body: C8yMeasurementCollection) throws -> AnyPublisher { + public func createMeasurement(body: C8yMeasurementCollection) -> AnyPublisher { var requestBody = body requestBody.next = nil requestBody.prev = nil requestBody.`self` = nil requestBody.statistics = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/measurement/measurements") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.measurementcollection+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.measurement+json, application/vnd.com.nsn.cumulocity.measurementcollection+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yMeasurementCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -266,7 +254,7 @@ public class MeasurementsApi: AdaptableApi { /// The managed object ID to which the measurement is associated. /// - type /// The type of measurement to search for. - public func deleteMeasurements(dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, source: String? = nil, type: String? = nil) throws -> AnyPublisher { + public func deleteMeasurements(dateFrom: String? = nil, dateTo: String? = nil, fragmentType: String? = nil, source: String? = nil, type: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } if let parameter = dateTo { queryItems.append(URLQueryItem(name: "dateTo", value: String(parameter))) } @@ -282,19 +270,12 @@ public class MeasurementsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -317,7 +298,7 @@ public class MeasurementsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the measurement. - public func getMeasurement(id: String) throws -> AnyPublisher { + public func getMeasurement(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/measurement/measurements/\(id)") .set(httpMethod: "get") @@ -326,21 +307,12 @@ public class MeasurementsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yMeasurement.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -365,7 +337,7 @@ public class MeasurementsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the measurement. - public func deleteMeasurement(id: String) throws -> AnyPublisher { + public func deleteMeasurement(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/measurement/measurements/\(id)") .set(httpMethod: "delete") @@ -374,24 +346,12 @@ public class MeasurementsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -422,12 +382,12 @@ public class MeasurementsApi: AdaptableApi { /// - dateTo /// End date or date and time of the measurement. /// - revert - /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the latest measurements first. By default, the results are sorted by the oldest measurements first. + /// If you are using a range query (that is, at least one of the `dateFrom` or `dateTo` parameters is included in the request), then setting `revert=true` will sort the results by the newest measurements first. By default, the results are sorted by the oldest measurements first. /// - series /// The specific series to search for. /// - 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) throws -> AnyPublisher { + public func getMeasurementSeries(aggregationType: String? = nil, dateFrom: String, dateTo: String, revert: Bool? = nil, series: [String]? = nil, source: String) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = aggregationType { queryItems.append(URLQueryItem(name: "aggregationType", value: String(parameter))) } queryItems.append(URLQueryItem(name: "dateFrom", value: String(dateFrom))) @@ -444,16 +404,12 @@ public class MeasurementsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yMeasurementSeries.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift b/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift index 5492a8d..065ede8 100644 --- a/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/NewDeviceRequestsApi.swift @@ -37,7 +37,7 @@ public class NewDeviceRequestsApi: 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 getNewDeviceRequests(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getNewDeviceRequests(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -52,16 +52,12 @@ public class NewDeviceRequestsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNewDeviceRequestCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -79,32 +75,36 @@ public class NewDeviceRequestsApi: AdaptableApi { /// A new device request was created. /// - 401 /// Authentication information is missing or invalid. + /// - 422 + /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createNewDeviceRequest(body: C8yNewDeviceRequest) throws -> AnyPublisher { + public func createNewDeviceRequest(body: C8yNewDeviceRequest) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/newDeviceRequests") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.newdevicerequest+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.newdevicerequest+json, application/vnd.com.nsn.cumulocity.error+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNewDeviceRequest.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -127,7 +127,7 @@ public class NewDeviceRequestsApi: AdaptableApi { /// - Parameters: /// - requestId /// Unique identifier of the new device request. - public func getNewDeviceRequest(requestId: String) throws -> AnyPublisher { + public func getNewDeviceRequest(requestId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/newDeviceRequests/\(requestId)") .set(httpMethod: "get") @@ -136,21 +136,12 @@ public class NewDeviceRequestsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNewDeviceRequest.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -175,35 +166,32 @@ public class NewDeviceRequestsApi: AdaptableApi { /// - body /// - requestId /// Unique identifier of the new device request. - public func updateNewDeviceRequest(body: C8yNewDeviceRequest, requestId: String) throws -> AnyPublisher { + public func updateNewDeviceRequest(body: C8yNewDeviceRequest, requestId: String) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/newDeviceRequests/\(requestId)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.newdevicerequest+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.newdevicerequest+json, application/vnd.com.nsn.cumulocity.error+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNewDeviceRequest.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -228,7 +216,7 @@ public class NewDeviceRequestsApi: AdaptableApi { /// - Parameters: /// - requestId /// Unique identifier of the new device request. - public func deleteNewDeviceRequest(requestId: String) throws -> AnyPublisher { + public func deleteNewDeviceRequest(requestId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/newDeviceRequests/\(requestId)") .set(httpMethod: "delete") @@ -237,24 +225,12 @@ public class NewDeviceRequestsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift b/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift index 615b2ab..efb44c2 100644 --- a/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/OperationsApi.swift @@ -59,7 +59,7 @@ public class OperationsApi: 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 getOperations(agentId: String? = nil, bulkOperationId: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, deviceId: String? = nil, fragmentType: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, status: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getOperations(agentId: String? = nil, bulkOperationId: String? = nil, currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, deviceId: String? = nil, fragmentType: String? = nil, pageSize: Int? = nil, revert: Bool? = nil, status: String? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = agentId { queryItems.append(URLQueryItem(name: "agentId", value: String(parameter))) } if let parameter = bulkOperationId { queryItems.append(URLQueryItem(name: "bulkOperationId", value: String(parameter))) } @@ -82,16 +82,12 @@ public class OperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOperationCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -99,6 +95,8 @@ public class OperationsApi: AdaptableApi { /// Create an operation /// Create an operation. /// + /// It is possible to add custom fragments to operations, for example `com_cumulocity_model_WebCamDevice` is a custom object of the webcam operation. + /// ///
Required roles
/// ROLE_DEVICE_CONTROL_ADMIN OR owner of the device OR ADMIN permissions on the device ///
@@ -109,9 +107,11 @@ public class OperationsApi: AdaptableApi { /// An operation was created. /// - 401 /// Authentication information is missing or invalid. + /// - 422 + /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createOperation(body: C8yOperation) throws -> AnyPublisher { + public func createOperation(body: C8yOperation) -> AnyPublisher { var requestBody = body requestBody.creationTime = nil requestBody.deviceExternalIDs?.`self` = nil @@ -119,26 +119,29 @@ public class OperationsApi: AdaptableApi { requestBody.failureReason = nil requestBody.`self` = nil requestBody.id = nil + requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/operations") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.operation+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.operation+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -171,7 +174,7 @@ public class OperationsApi: AdaptableApi { /// The ID of the device the operation is performed for. /// - status /// Status of the operation. - public func deleteOperations(agentId: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, deviceId: String? = nil, status: String? = nil) throws -> AnyPublisher { + public func deleteOperations(agentId: String? = nil, dateFrom: String? = nil, dateTo: String? = nil, deviceId: String? = nil, status: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = agentId { queryItems.append(URLQueryItem(name: "agentId", value: String(parameter))) } if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } @@ -187,19 +190,12 @@ public class OperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -222,7 +218,7 @@ public class OperationsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the operation. - public func getOperation(id: String) throws -> AnyPublisher { + public func getOperation(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/operations/\(id)") .set(httpMethod: "get") @@ -231,21 +227,12 @@ public class OperationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -272,46 +259,37 @@ public class OperationsApi: AdaptableApi { /// - body /// - id /// Unique identifier of the operation. - public func updateOperation(body: C8yOperation, id: String) throws -> AnyPublisher { + public func updateOperation(body: C8yOperation, id: String) -> AnyPublisher { var requestBody = body requestBody.creationTime = nil requestBody.deviceExternalIDs?.`self` = nil - requestBody.comCumulocityModelWebCamDevice = nil requestBody.bulkOperationId = nil requestBody.failureReason = nil requestBody.`self` = nil requestBody.id = nil requestBody.deviceId = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/devicecontrol/operations/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.operation+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.operation+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - let decoder = JSONDecoder() - let error422 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error422) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOperation.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift index 867dc26..33d9e38 100644 --- a/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/OptionsApi.swift @@ -35,7 +35,7 @@ public class OptionsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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 getOptions(currentPage: Int? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getOptions(currentPage: Int? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -49,16 +49,12 @@ public class OptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOptionCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -112,32 +108,31 @@ public class OptionsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createOption(body: C8yOption) throws -> AnyPublisher { + public func createOption(body: C8yOption) -> 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/options") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.option+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.option+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOption.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -158,7 +153,7 @@ public class OptionsApi: AdaptableApi { /// - Parameters: /// - category /// The category of the options. - public func getOptionsByCategory(category: String) throws -> AnyPublisher { + public func getOptionsByCategory(category: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/options/\(category)") .set(httpMethod: "get") @@ -167,16 +162,12 @@ public class OptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yCategoryOptions.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -200,31 +191,30 @@ public class OptionsApi: AdaptableApi { /// - body /// - category /// The category of the options. - public func updateOptionsByCategory(body: C8yCategoryOptions, category: String) throws -> AnyPublisher { + public func updateOptionsByCategory(body: C8yCategoryOptions, category: 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/options/\(category)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.option+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yCategoryOptions.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -249,7 +239,7 @@ public class OptionsApi: AdaptableApi { /// The category of the options. /// - key /// The key of an option. - public func getOption(category: String, key: String) throws -> AnyPublisher { + public func getOption(category: String, key: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/options/\(category)/\(key)") .set(httpMethod: "get") @@ -258,19 +248,12 @@ public class OptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - throw Errors.badResponseError(response: httpResponse, reason: "Option not found.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOption.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -298,34 +281,30 @@ public class OptionsApi: AdaptableApi { /// The category of the options. /// - key /// The key of an option. - public func updateOption(body: C8yCategoryKeyOption, category: String, key: String) throws -> AnyPublisher { + public func updateOption(body: C8yCategoryKeyOption, category: String, key: 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/options/\(category)/\(key)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.option+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - throw Errors.badResponseError(response: httpResponse, reason: "Option not found.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yOption.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -350,7 +329,7 @@ public class OptionsApi: AdaptableApi { /// The category of the options. /// - key /// The key of an option. - public func deleteOption(category: String, key: String) throws -> AnyPublisher { + public func deleteOption(category: String, key: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/options/\(category)/\(key)") .set(httpMethod: "delete") @@ -359,19 +338,12 @@ public class OptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - throw Errors.badResponseError(response: httpResponse, reason: "Option not found.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift b/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift index e6c3c47..567b8d7 100644 --- a/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/RealtimeNotificationApi.swift @@ -227,35 +227,34 @@ public class RealtimeNotificationApi: AdaptableApi { /// Authentication information is missing or invalid. /// - Parameters: /// - body - public func createRealtimeNotification(body: C8yRealtimeNotification) throws -> AnyPublisher { + public func createRealtimeNotification(body: C8yRealtimeNotification) -> AnyPublisher { var requestBody = body requestBody.clientId = nil requestBody.data = nil requestBody.error = nil requestBody.successful = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/notification/realtime") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 400 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRealtimeNotification.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/RetentionRulesApi.swift b/Sources/CumulocityCoreLibrary/Api/RetentionRulesApi.swift index 0bf87b7..7b5cf9b 100644 --- a/Sources/CumulocityCoreLibrary/Api/RetentionRulesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/RetentionRulesApi.swift @@ -39,7 +39,7 @@ public class RetentionRulesApi: 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 getRetentionRules(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getRetentionRules(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -54,19 +54,12 @@ public class RetentionRulesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRetentionRuleCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -90,36 +83,32 @@ public class RetentionRulesApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createRetentionRule(body: C8yRetentionRule) throws -> AnyPublisher { + public func createRetentionRule(body: C8yRetentionRule) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/retention/retentions") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.retentionrule+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.retentionrule+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRetentionRule.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -144,7 +133,7 @@ public class RetentionRulesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the retention rule. - public func getRetentionRule(id: String) throws -> AnyPublisher { + public func getRetentionRule(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/retention/retentions/\(id)") .set(httpMethod: "get") @@ -153,24 +142,12 @@ public class RetentionRulesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRetentionRule.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -198,41 +175,32 @@ public class RetentionRulesApi: AdaptableApi { /// - body /// - id /// Unique identifier of the retention rule. - public func updateRetentionRule(body: C8yRetentionRule, id: String) throws -> AnyPublisher { + public func updateRetentionRule(body: C8yRetentionRule, id: String) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/retention/retentions/\(id)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.retentionrule+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.retentionrule+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRetentionRule.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -257,7 +225,7 @@ public class RetentionRulesApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the retention rule. - public func deleteRetentionRule(id: String) throws -> AnyPublisher { + public func deleteRetentionRule(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/retention/retentions/\(id)") .set(httpMethod: "delete") @@ -266,24 +234,12 @@ public class RetentionRulesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/RolesApi.swift b/Sources/CumulocityCoreLibrary/Api/RolesApi.swift index 3f4df2b..24d0620 100644 --- a/Sources/CumulocityCoreLibrary/Api/RolesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/RolesApi.swift @@ -37,7 +37,7 @@ public class RolesApi: 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 getUserRoles(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getUserRoles(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -52,16 +52,12 @@ public class RolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUserRoleCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -84,7 +80,7 @@ public class RolesApi: AdaptableApi { /// - Parameters: /// - name /// The name of the user role. - public func getUserRole(name: String) throws -> AnyPublisher { + public func getUserRole(name: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/roles/\(name)") .set(httpMethod: "get") @@ -93,21 +89,12 @@ public class RolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRole.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -138,7 +125,7 @@ public class RolesApi: AdaptableApi { /// The current page of the paginated results. /// - pageSize /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. - public func getGroupRoles(tenantId: String, groupId: Int, currentPage: Int? = nil, pageSize: Int? = nil) throws -> AnyPublisher { + public func getGroupRoles(tenantId: String, groupId: Int, currentPage: Int? = nil, pageSize: Int? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -151,26 +138,12 @@ public class RolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRoleReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -202,42 +175,30 @@ public class RolesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupId /// Unique identifier of the user group. - public func assignGroupRole(body: C8ySubscribedRole, tenantId: String, groupId: Int) throws -> AnyPublisher { + public func assignGroupRole(body: C8ySubscribedRole, tenantId: String, groupId: Int) -> 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)/groups/\(groupId)/roles") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.rolereference+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.rolereference+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Conflict – Role already assigned to the user group.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRoleReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -266,7 +227,7 @@ public class RolesApi: AdaptableApi { /// Unique identifier of the user group. /// - roleId /// Unique identifier of the user role. - public func unassignGroupRole(tenantId: String, groupId: Int, roleId: String) throws -> AnyPublisher { + public func unassignGroupRole(tenantId: String, groupId: Int, roleId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups/\(groupId)/roles/\(roleId)") .set(httpMethod: "delete") @@ -275,24 +236,12 @@ public class RolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -324,41 +273,30 @@ public class RolesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func assignUserRole(body: C8ySubscribedRole, tenantId: String, userId: String) throws -> AnyPublisher { + public func assignUserRole(body: C8ySubscribedRole, 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)/roles") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.rolereference+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.rolereference+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yRoleReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -387,7 +325,7 @@ public class RolesApi: AdaptableApi { /// Unique identifier of the a user. /// - roleId /// Unique identifier of the user role. - public func unassignUserRole(tenantId: String, userId: String, roleId: String) throws -> AnyPublisher { + public func unassignUserRole(tenantId: String, userId: String, roleId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)/roles/\(roleId)") .set(httpMethod: "delete") @@ -396,24 +334,12 @@ public class RolesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift index bc597cc..2011660 100644 --- a/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/SubscriptionsApi.swift @@ -38,7 +38,7 @@ public class SubscriptionsApi: AdaptableApi { /// The managed object ID to which the subscription is associated. /// - 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) throws -> AnyPublisher { + public func getSubscriptions(context: String? = nil, currentPage: Int? = nil, pageSize: Int? = nil, source: String? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = context { queryItems.append(URLQueryItem(name: "context", value: String(parameter))) } if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } @@ -54,21 +54,12 @@ public class SubscriptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNotificationSubscriptionCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -104,49 +95,33 @@ public class SubscriptionsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createSubscription(body: C8yNotificationSubscription) throws -> AnyPublisher { + public func createSubscription(body: C8yNotificationSubscription) -> AnyPublisher { var requestBody = body requestBody.`self` = nil requestBody.id = nil requestBody.source?.`self` = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/notification2/subscriptions") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.subscription+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.subscription+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - let decoder = JSONDecoder() - let error409 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error409) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNotificationSubscription.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -175,7 +150,7 @@ public class SubscriptionsApi: AdaptableApi { /// The context to which the subscription is associated. > **ⓘ Info:** If the value is `mo`, then `source` must also be provided in the query. /// - source /// The managed object ID to which the subscription is associated. - public func deleteSubscriptions(context: String? = nil, source: String? = nil) throws -> AnyPublisher { + public func deleteSubscriptions(context: String? = nil, source: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = context { queryItems.append(URLQueryItem(name: "context", value: String(parameter))) } if let parameter = source { queryItems.append(URLQueryItem(name: "source", value: String(parameter))) } @@ -188,24 +163,12 @@ public class SubscriptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – error in query parameters") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -230,7 +193,7 @@ public class SubscriptionsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the notification subscription. - public func getSubscription(id: String) throws -> AnyPublisher { + public func getSubscription(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/notification2/subscriptions/\(id)") .set(httpMethod: "get") @@ -239,26 +202,12 @@ public class SubscriptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNotificationSubscription.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -283,7 +232,7 @@ public class SubscriptionsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the notification subscription. - public func deleteSubscription(id: String) throws -> AnyPublisher { + public func deleteSubscription(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/notification2/subscriptions/\(id)") .set(httpMethod: "delete") @@ -292,26 +241,12 @@ public class SubscriptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/SystemOptionsApi.swift b/Sources/CumulocityCoreLibrary/Api/SystemOptionsApi.swift index af7b929..6771002 100644 --- a/Sources/CumulocityCoreLibrary/Api/SystemOptionsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/SystemOptionsApi.swift @@ -20,7 +20,7 @@ public class SystemOptionsApi: AdaptableApi { /// The request has succeeded and the system options are sent in the response. /// - 401 /// Authentication information is missing or invalid. - public func getSystemOptions() throws -> AnyPublisher { + public func getSystemOptions() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/system/options") .set(httpMethod: "get") @@ -29,16 +29,12 @@ public class SystemOptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8ySystemOptionCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -56,7 +52,7 @@ public class SystemOptionsApi: AdaptableApi { /// The category of the system options. /// - key /// The key of a system option. - public func getSystemOption(category: String, key: String) throws -> AnyPublisher { + public func getSystemOption(category: String, key: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/system/options/\(category)/\(key)") .set(httpMethod: "get") @@ -65,16 +61,12 @@ public class SystemOptionsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8ySystemOption.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/TenantApplicationsApi.swift b/Sources/CumulocityCoreLibrary/Api/TenantApplicationsApi.swift index d86f403..193aafd 100644 --- a/Sources/CumulocityCoreLibrary/Api/TenantApplicationsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TenantApplicationsApi.swift @@ -42,7 +42,7 @@ public class TenantApplicationsApi: 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 getSubscribedApplications(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getSubscribedApplications(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -57,24 +57,12 @@ public class TenantApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -100,36 +88,30 @@ public class TenantApplicationsApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func subscribeApplication(body: C8ySubscribedApplicationReference, tenantId: String) throws -> AnyPublisher { + public func subscribeApplication(body: C8ySubscribedApplicationReference, 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)/applications") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.applicationreference+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.applicationreference+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "The application is already assigned to the tenant.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yApplicationReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -154,7 +136,7 @@ public class TenantApplicationsApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - applicationId /// Unique identifier of the application. - public func unsubscribeApplication(tenantId: String, applicationId: String) throws -> AnyPublisher { + public func unsubscribeApplication(tenantId: String, applicationId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/applications/\(applicationId)") .set(httpMethod: "delete") @@ -163,21 +145,12 @@ public class TenantApplicationsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift b/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift index 70a084d..dfe2582 100644 --- a/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TenantsApi.swift @@ -62,7 +62,7 @@ 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) throws -> AnyPublisher { + public func getTenants(currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -77,16 +77,12 @@ public class TenantsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenantCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -112,7 +108,7 @@ public class TenantsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createTenant(body: C8yTenant) throws -> AnyPublisher { + public func createTenant(body: C8yTenant) -> AnyPublisher { var requestBody = body requestBody.allowCreateTenants = nil requestBody.parent = nil @@ -122,35 +118,28 @@ public class TenantsApi: AdaptableApi { requestBody.ownedApplications = nil requestBody.applications = nil requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.tenant+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.tenant+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Conflict – The tenant domain/ID already exists.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenant.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -168,7 +157,7 @@ public class TenantsApi: AdaptableApi { /// The request has succeeded and the information is sent in the response. /// - 401 /// Authentication information is missing or invalid. - public func getCurrentTenant() throws -> AnyPublisher { + public func getCurrentTenant() -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/currentTenant") .set(httpMethod: "get") @@ -177,16 +166,12 @@ public class TenantsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yCurrentTenant.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -211,7 +196,7 @@ public class TenantsApi: AdaptableApi { /// - Parameters: /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func getTenant(tenantId: String) throws -> AnyPublisher { + public func getTenant(tenantId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)") .set(httpMethod: "get") @@ -220,24 +205,12 @@ public class TenantsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenant.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -265,7 +238,7 @@ public class TenantsApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func updateTenant(body: C8yTenant, tenantId: String) throws -> AnyPublisher { + public func updateTenant(body: C8yTenant, tenantId: String) -> AnyPublisher { var requestBody = body requestBody.adminName = nil requestBody.allowCreateTenants = nil @@ -276,37 +249,28 @@ public class TenantsApi: AdaptableApi { requestBody.ownedApplications = nil requestBody.applications = nil requestBody.status = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.tenant+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.tenant+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenant.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -335,7 +299,7 @@ public class TenantsApi: AdaptableApi { /// - Parameters: /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func deleteTenant(tenantId: String) throws -> AnyPublisher { + public func deleteTenant(tenantId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)") .set(httpMethod: "delete") @@ -344,25 +308,50 @@ public class TenantsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) + return element.data + }).eraseToAnyPublisher() + } + + /// Retrieve TFA settings of a specific tenant + /// Retrieve the two-factor authentication settings of a specific tenant by a given tenant ID. + /// + ///
Required roles
+ /// ((ROLE_TENANT_MANAGEMENT_READ OR ROLE_USER_MANAGEMENT_READ) AND (the current tenant is its parent OR is the management tenant OR the current user belongs to the tenant)) OR (the user belongs to the tenant AND ROLE_USER_MANAGEMENT_OWN_READ) + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The request has succeeded and the TFA settings are sent in the response. + /// - 401 + /// Authentication information is missing or invalid. + /// - 404 + /// Tenant not found. + /// - Parameters: + /// - tenantId + /// Unique identifier of a Cumulocity IoT tenant. + public func getTenantTfaSettings(tenantId: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/tenants/\(tenantId)/tfa") + .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) } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data - }).eraseToAnyPublisher() + }).decode(type: C8yTenantTfaData.self, decoder: JSONDecoder()).eraseToAnyPublisher() } } diff --git a/Sources/CumulocityCoreLibrary/Api/TokensApi.swift b/Sources/CumulocityCoreLibrary/Api/TokensApi.swift index c3413c2..5e21aa8 100644 --- a/Sources/CumulocityCoreLibrary/Api/TokensApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TokensApi.swift @@ -37,36 +37,30 @@ public class TokensApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func createToken(body: C8yNotificationTokenClaims) throws -> AnyPublisher { + public func createToken(body: C8yNotificationTokenClaims) -> 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: "/notification2/token") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yNotificationToken.self, decoder: JSONDecoder()).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift b/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift index d2a1404..2406a0e 100644 --- a/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/TrustedCertificatesApi.swift @@ -45,7 +45,7 @@ public class TrustedCertificatesApi: 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 getTrustedCertificates(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getTrustedCertificates(tenantId: String, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -60,24 +60,12 @@ public class TrustedCertificatesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTrustedCertificateCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -105,7 +93,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func addTrustedCertificate(body: C8yTrustedCertificate, tenantId: String) throws -> AnyPublisher { + public func addTrustedCertificate(body: C8yTrustedCertificate, tenantId: String) -> AnyPublisher { var requestBody = body requestBody.notAfter = nil requestBody.serialNumber = nil @@ -116,37 +104,28 @@ public class TrustedCertificatesApi: AdaptableApi { requestBody.version = nil requestBody.issuer = nil requestBody.notBefore = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicate – A certificate with the same fingerprint already exists.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – Invalid certificate data.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -174,43 +153,34 @@ public class TrustedCertificatesApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func addTrustedCertificates(body: C8yTrustedCertificateCollection, tenantId: String) throws -> AnyPublisher { + public func addTrustedCertificates(body: C8yTrustedCertificateCollection, tenantId: String) -> AnyPublisher { var requestBody = body requestBody.next = nil requestBody.prev = nil requestBody.`self` = nil requestBody.statistics = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates/bulk") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicate – A certificate with the same fingerprint already exists.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – Invalid certificates data.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTrustedCertificateCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -233,7 +203,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - fingerprint /// Unique identifier of a trusted certificate. - public func getTrustedCertificate(tenantId: String, fingerprint: String) throws -> AnyPublisher { + public func getTrustedCertificate(tenantId: String, fingerprint: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates/\(fingerprint)") .set(httpMethod: "get") @@ -242,16 +212,12 @@ public class TrustedCertificatesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -279,7 +245,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - fingerprint /// Unique identifier of a trusted certificate. - public func updateTrustedCertificate(body: C8yTrustedCertificate, tenantId: String, fingerprint: String) throws -> AnyPublisher { + public func updateTrustedCertificate(body: C8yTrustedCertificate, tenantId: String, fingerprint: String) -> AnyPublisher { var requestBody = body requestBody.notAfter = nil requestBody.serialNumber = nil @@ -291,32 +257,28 @@ public class TrustedCertificatesApi: AdaptableApi { requestBody.version = nil requestBody.issuer = nil requestBody.notBefore = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates/\(fingerprint)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - throw Errors.badResponseError(response: httpResponse, reason: "Certificate not found.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -341,7 +303,7 @@ public class TrustedCertificatesApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - fingerprint /// Unique identifier of a trusted certificate. - public func removeTrustedCertificate(tenantId: String, fingerprint: String) throws -> AnyPublisher { + public func removeTrustedCertificate(tenantId: String, fingerprint: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates/\(fingerprint)") .set(httpMethod: "delete") @@ -350,20 +312,146 @@ public class TrustedCertificatesApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) } - guard httpResponse.statusCode != 404 else { - throw Errors.badResponseError(response: httpResponse, reason: "Certificate not found.") + return element.data + }).eraseToAnyPublisher() + } + + /// Provide the proof of possession for an already uploaded certificate + /// Provide the proof of possession for a specific uploaded certificate (by a given fingerprint) for a specific tenant (by a given ID). + /// + ///
+ /// (ROLE_TENANT_MANAGEMENT_ADMIN OR ROLE_TENANT_ADMIN) AND is the current tenant + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The provided signed verification code check was successful. + /// - 400 + /// The provided signed verification code is not correct. + /// - 401 + /// Authentication information is missing or invalid. + /// - 404 + /// Trusted certificate not found. + /// - 422 + /// Proof of possession for the certificate was not confirmed. + /// - Parameters: + /// - body + /// - tenantId + /// Unique identifier of a Cumulocity IoT tenant. + /// - fingerprint + /// Unique identifier of a trusted certificate. + public func proveCertificatePossession(body: C8yUploadedTrustedCertSignedVerificationCode, tenantId: String, fingerprint: 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)/trusted-certificates-pop/\(fingerprint)/pop") + .set(httpMethod: "post") + .add(header: "Content-Type", value: "application/json") + .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, 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) } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data - }).eraseToAnyPublisher() + }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Confirm an already uploaded certificate + /// Confirm an already uploaded certificate (by a given fingerprint) for a specific tenant (by a given ID). + /// + ///
+ /// (ROLE_TENANT_MANAGEMENT_ADMIN OR ROLE_TENANT_ADMIN) AND is the management tenant + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The certificate is confirmed. + /// - 401 + /// Authentication information is missing or invalid. + /// - 404 + /// Trusted certificate not found. + /// - 422 + /// The verification was not successful. Certificate not confirmed. + /// - Parameters: + /// - tenantId + /// Unique identifier of a Cumulocity IoT tenant. + /// - fingerprint + /// Unique identifier of a trusted certificate. + public func confirmCertificate(tenantId: String, fingerprint: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates-pop/\(fingerprint)/confirmed") + .set(httpMethod: "post") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + + /// Generate a verification code for the proof of possession operation for the given certificate + /// Generate a verification code for the proof of possession operation for the certificate (by a given fingerprint). + /// + ///
+ /// (ROLE_TENANT_MANAGEMENT_ADMIN OR ROLE_TENANT_ADMIN) AND is the current tenant + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The verification code was generated. + /// - 401 + /// Authentication information is missing or invalid. + /// - 404 + /// Trusted certificate not found. + /// - Parameters: + /// - tenantId + /// Unique identifier of a Cumulocity IoT tenant. + /// - fingerprint + /// Unique identifier of a trusted certificate. + public func generateVerificationCode(tenantId: String, fingerprint: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/tenant/tenants/\(tenantId)/trusted-certificates-pop/\(fingerprint)/verification-code") + .set(httpMethod: "post") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).decode(type: C8yTrustedCertificate.self, decoder: JSONDecoder()).eraseToAnyPublisher() } } diff --git a/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift b/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift index 3796dd8..d7319a4 100644 --- a/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/UsageStatisticsApi.swift @@ -99,7 +99,7 @@ public class UsageStatisticsApi: 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 getTenantUsageStatisticsCollectionResource(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getTenantUsageStatisticsCollectionResource(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } @@ -116,16 +116,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenantUsageStatisticsCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -145,7 +141,7 @@ public class UsageStatisticsApi: AdaptableApi { /// End date or date and time of the statistics. /// - tenant /// Unique identifier of a Cumulocity IoT tenant. - public func getTenantUsageStatistics(dateFrom: String? = nil, dateTo: String? = nil, tenant: String? = nil) throws -> AnyPublisher { + public func getTenantUsageStatistics(dateFrom: String? = nil, dateTo: String? = nil, tenant: String? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } if let parameter = dateTo { queryItems.append(URLQueryItem(name: "dateTo", value: String(parameter))) } @@ -159,16 +155,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8ySummaryTenantUsageStatistics.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -191,7 +183,7 @@ public class UsageStatisticsApi: AdaptableApi { /// Start date or date and time of the statistics. /// - dateTo /// End date or date and time of the statistics. - public func getTenantsUsageStatistics(dateFrom: String? = nil, dateTo: String? = nil) throws -> AnyPublisher<[C8ySummaryAllTenantsUsageStatistics], Swift.Error> { + public func getTenantsUsageStatistics(dateFrom: String? = nil, dateTo: String? = nil) -> AnyPublisher<[C8ySummaryAllTenantsUsageStatistics], Error> { var queryItems: [URLQueryItem] = [] if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } if let parameter = dateTo { queryItems.append(URLQueryItem(name: "dateTo", value: String(parameter))) } @@ -204,16 +196,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: [C8ySummaryAllTenantsUsageStatistics].self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -242,7 +230,7 @@ public class UsageStatisticsApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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 getMetadata(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getMetadata(currentPage: Int? = nil, dateFrom: String? = nil, dateTo: String? = nil, pageSize: Int? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = dateFrom { queryItems.append(URLQueryItem(name: "dateFrom", value: String(parameter))) } @@ -258,16 +246,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yTenantUsageStatisticsFileCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -292,31 +276,30 @@ public class UsageStatisticsApi: AdaptableApi { /// Unprocessable Entity – invalid payload. /// - Parameters: /// - body - public func generateStatisticsFile(body: C8yRangeStatisticsFile) throws -> AnyPublisher { + public func generateStatisticsFile(body: C8yRangeStatisticsFile) -> 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/statistics/files") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.tenantstatisticsdate+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.tenantstatisticsfile+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yStatisticsFile.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -339,7 +322,7 @@ public class UsageStatisticsApi: AdaptableApi { /// - Parameters: /// - id /// Unique identifier of the statistics file. - public func getStatisticsFile(id: String) throws -> AnyPublisher { + public func getStatisticsFile(id: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/statistics/files/\(id)") .set(httpMethod: "get") @@ -348,21 +331,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -387,7 +361,7 @@ public class UsageStatisticsApi: AdaptableApi { /// - Parameters: /// - month /// Date (format YYYY-MM-dd) specifying the month for which the statistics file will be downloaded (the day value is ignored). - public func getLatestStatisticsFile(month: Date) throws -> AnyPublisher { + public func getLatestStatisticsFile(month: Date) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/tenant/statistics/files/latest/\(month)") .set(httpMethod: "get") @@ -396,16 +370,12 @@ public class UsageStatisticsApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Api/UsersApi.swift b/Sources/CumulocityCoreLibrary/Api/UsersApi.swift index 47f3487..a2e60b1 100644 --- a/Sources/CumulocityCoreLibrary/Api/UsersApi.swift +++ b/Sources/CumulocityCoreLibrary/Api/UsersApi.swift @@ -51,7 +51,7 @@ public class UsersApi: 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 getUsers(tenantId: String, currentPage: Int? = nil, groups: [String]? = nil, onlyDevices: Bool? = nil, owner: String? = nil, pageSize: Int? = nil, username: String? = nil, withSubusersCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) throws -> AnyPublisher { + public func getUsers(tenantId: String, currentPage: Int? = nil, groups: [String]? = nil, onlyDevices: Bool? = nil, owner: String? = nil, pageSize: Int? = nil, username: String? = nil, withSubusersCount: Bool? = nil, withTotalElements: Bool? = nil, withTotalPages: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = groups { parameter.forEach{ p in queryItems.append(URLQueryItem(name: "groups", value: p)) } } @@ -71,21 +71,12 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUserCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -113,7 +104,7 @@ public class UsersApi: AdaptableApi { /// - body /// - tenantId /// Unique identifier of a Cumulocity IoT tenant. - public func createUser(body: C8yUser, tenantId: String) throws -> AnyPublisher { + public func createUser(body: C8yUser, tenantId: String) -> AnyPublisher { var requestBody = body requestBody.passwordStrength = nil requestBody.roles = nil @@ -122,39 +113,31 @@ public class UsersApi: AdaptableApi { requestBody.shouldResetPassword = nil requestBody.id = nil requestBody.lastPasswordChange = nil + requestBody.twoFactorAuthenticationEnabled = nil requestBody.devicePermissions = nil requestBody.applications = nil + var encodedRequestBody: Data? = nil + do { + encodedRequestBody = try JSONEncoder().encode(requestBody) + } catch { + return Fail(error: error).eraseToAnyPublisher() + } let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.user+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.user+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 409 else { - throw Errors.badResponseError(response: httpResponse, reason: "Duplicate – The userName or alias already exists.") - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -185,7 +168,7 @@ public class UsersApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func getUser(tenantId: String, userId: String) throws -> AnyPublisher { + public func getUser(tenantId: String, userId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)") .set(httpMethod: "get") @@ -194,26 +177,12 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -247,7 +216,7 @@ public class UsersApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func updateUser(body: C8yUser, tenantId: String, userId: String) throws -> AnyPublisher { + public func updateUser(body: C8yUser, tenantId: String, userId: String) -> AnyPublisher { var requestBody = body requestBody.passwordStrength = nil requestBody.roles = nil @@ -257,41 +226,31 @@ public class UsersApi: AdaptableApi { requestBody.id = nil requestBody.lastPasswordChange = nil requestBody.userName = nil + requestBody.twoFactorAuthenticationEnabled = nil requestBody.devicePermissions = nil requestBody.applications = nil + 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)") .set(httpMethod: "put") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.user+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.user+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -318,7 +277,7 @@ public class UsersApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - userId /// Unique identifier of the a user. - public func deleteUser(tenantId: String, userId: String) throws -> AnyPublisher { + public func deleteUser(tenantId: String, userId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/users/\(userId)") .set(httpMethod: "delete") @@ -327,28 +286,112 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") + guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) + return element.data + }).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. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_ADMIN OR ROLE_USER_MANAGEMENT_CREATE AND has access to device permissions and applications + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// A user was updated. + /// - 401 + /// Authentication information is missing or invalid. + /// - 403 + /// Not enough permissions/roles to perform this operation. + /// - 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) } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } + /// Retrieve the TFA settings of a specific user + /// Retrieve the two-factor authentication settings for the specified user. + /// + ///
Required roles
+ /// ROLE_USER_MANAGEMENT_READ OR (ROLE_USER_MANAGEMENT_CREATE AND is parent of the user) OR is the current user + ///
+ /// + /// The following table gives an overview of the possible response codes and their meanings. + /// - Returns: + /// - 200 + /// The request has succeeded and the TFA settings are sent in the response. + /// - 401 + /// Authentication information is missing or invalid. + /// - 403 + /// Not enough permissions/roles to perform this operation. + /// - 404 + /// User not found. + /// - Parameters: + /// - tenantId + /// Unique identifier of a Cumulocity IoT tenant. + /// - userId + /// Unique identifier of the a user. + public func getUserTfaSettings(tenantId: String, userId: String) -> AnyPublisher { + let builder = URLRequestBuilder() + .set(resourcePath: "/user/\(tenantId)/users/\(userId)/tfa") + .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) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } + throw Errors.undescribedError(response: httpResponse) + } + return element.data + }).decode(type: C8yUserTfaData.self, decoder: JSONDecoder()).eraseToAnyPublisher() + } + /// Retrieve a user by username in a specific tenant /// Retrieve a user by username in a specific tenant (by a given tenant ID). /// @@ -371,7 +414,7 @@ public class UsersApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - username /// The username of the a user. - public func getUserByUsername(tenantId: String, username: String) throws -> AnyPublisher { + public func getUserByUsername(tenantId: String, username: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/userByName/\(username)") .set(httpMethod: "get") @@ -380,26 +423,12 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUser.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -432,7 +461,7 @@ public class UsersApi: AdaptableApi { /// Indicates how many entries of the collection shall be returned. The upper limit for one page is 2,000 objects. /// - 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)). - public func getUsersFromUserGroup(tenantId: String, groupId: Int, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) throws -> AnyPublisher { + public func getUsersFromUserGroup(tenantId: String, groupId: Int, currentPage: Int? = nil, pageSize: Int? = nil, withTotalElements: Bool? = nil) -> AnyPublisher { var queryItems: [URLQueryItem] = [] if let parameter = currentPage { queryItems.append(URLQueryItem(name: "currentPage", value: String(parameter))) } if let parameter = pageSize { queryItems.append(URLQueryItem(name: "pageSize", value: String(parameter))) } @@ -446,26 +475,12 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUserReferenceCollection.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -495,41 +510,30 @@ public class UsersApi: AdaptableApi { /// Unique identifier of a Cumulocity IoT tenant. /// - groupId /// Unique identifier of the user group. - public func assignUserToUserGroup(body: C8ySubscribedUser, tenantId: String, groupId: Int) throws -> AnyPublisher { + public func assignUserToUserGroup(body: C8ySubscribedUser, tenantId: String, groupId: Int) -> 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)/groups/\(groupId)/users") .set(httpMethod: "post") .add(header: "Content-Type", value: "application/vnd.com.nsn.cumulocity.userreference+json") .add(header: "Accept", value: "application/vnd.com.nsn.cumulocity.error+json, application/vnd.com.nsn.cumulocity.userreference+json") - .set(httpBody: try JSONEncoder().encode(requestBody)) + .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - let decoder = JSONDecoder() - let error403 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error403) - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - guard httpResponse.statusCode != 422 else { - throw Errors.badResponseError(response: httpResponse, reason: "Unprocessable Entity – invalid payload.") - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).decode(type: C8yUserReference.self, decoder: JSONDecoder()).eraseToAnyPublisher() } @@ -558,7 +562,7 @@ public class UsersApi: AdaptableApi { /// Unique identifier of the user group. /// - userId /// Unique identifier of the a user. - public func removeUserFromUserGroup(tenantId: String, groupId: Int, userId: String) throws -> AnyPublisher { + public func removeUserFromUserGroup(tenantId: String, groupId: Int, userId: String) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/\(tenantId)/groups/\(groupId)/users/\(userId)") .set(httpMethod: "delete") @@ -567,24 +571,12 @@ public class UsersApi: AdaptableApi { guard let httpResponse = element.response as? HTTPURLResponse else { throw URLError(.badServerResponse) } - guard httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - guard httpResponse.statusCode != 403 else { - throw Errors.badResponseError(response: httpResponse, reason: "Not authorized to perform this operation.") - } - guard httpResponse.statusCode != 404 else { - let decoder = JSONDecoder() - let error404 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error404) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } @@ -605,27 +597,23 @@ public class UsersApi: AdaptableApi { /// The authorization cookie storing the access token of the user. This parameter is specific to OAI-Secure authentication. /// - xXSRFTOKEN /// Prevents XRSF attack of the authenticated user. This parameter is specific to OAI-Secure authentication. - public func logout(cookie: String? = nil, xXSRFTOKEN: String? = nil) throws -> AnyPublisher { + public func logout(cookie: String? = nil, xXSRFTOKEN: String? = nil) -> AnyPublisher { let builder = URLRequestBuilder() .set(resourcePath: "/user/logout") .set(httpMethod: "post") - .add(header: "Cookie", value: "\(cookie)") - .add(header: "X-XSRF-TOKEN", value: "\(xXSRFTOKEN)") + .add(header: "Cookie", value: String(describing: cookie)) + .add(header: "X-XSRF-TOKEN", value: String(describing: xXSRFTOKEN)) .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 httpResponse.statusCode != 401 else { - let decoder = JSONDecoder() - let error401 = try decoder.decode(C8yError.self, from: element.data) - throw Errors.badResponseError(response: httpResponse, reason: error401) - } - // generic error fallback guard (200..<300) ~= httpResponse.statusCode else { + if let c8yError = try? JSONDecoder().decode(C8yError.self, from: element.data) { + throw Errors.badResponseError(response: httpResponse, reason: c8yError) + } throw Errors.undescribedError(response: httpResponse) } - return element.data }).eraseToAnyPublisher() } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift b/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift index 3933a87..84202a8 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAlarm.swift @@ -26,7 +26,7 @@ public struct C8yAlarm: Codable { self.type = try container.decodeIfPresent(String.self, forKey: .type) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yAlarm.decoders { - self.customFragments?[typeName] = try? decoder(additionalContainer) + self.customFragments[typeName] = try? decoder(additionalContainer) } } } @@ -47,7 +47,7 @@ public struct C8yAlarm: Codable { try container.encodeIfPresent(self.type, forKey: .type) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yAlarm.encoders { - if let property = self.customFragments?[typeName] { + if let property = self.customFragments[typeName] { try encoder(property, &additionalContainer) } } @@ -93,7 +93,16 @@ public struct C8yAlarm: Codable { /// /// 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. /// - public var customFragments: [String: Any]? = [:] + public var customFragments: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case count diff --git a/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift b/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift index 290fba5..c98260f 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yAuditRecord.swift @@ -27,7 +27,7 @@ public struct C8yAuditRecord: Codable { self.user = try container.decodeIfPresent(String.self, forKey: .user) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yAuditRecord.decoders { - self.customProperties?[typeName] = try? decoder(additionalContainer) + self.customProperties[typeName] = try? decoder(additionalContainer) } } } @@ -49,7 +49,7 @@ public struct C8yAuditRecord: Codable { try container.encodeIfPresent(self.user, forKey: .user) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yAuditRecord.encoders { - if let property = self.customProperties?[typeName] { + if let property = self.customProperties[typeName] { try encoder(property, &additionalContainer) } } @@ -96,7 +96,16 @@ public struct C8yAuditRecord: 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 can be of any type, for example, object or string. /// - public var customProperties: [String: Any]? = [:] + public var customProperties: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customProperties[key] + } + set(newValue) { + customProperties[key] = newValue + } + } enum CodingKeys: String, CodingKey { case activity diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift b/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift index 9539dee..9c9a3b9 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yCategoryOptions.swift @@ -13,7 +13,7 @@ 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 { - self.keyValuePairs?[typeName] = try? decoder(additionalContainer) + self.keyValuePairs[typeName] = try? decoder(additionalContainer) } } } @@ -21,14 +21,23 @@ public struct C8yCategoryOptions: Codable { public func encode(to encoder: Encoder) throws { var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yCategoryOptions.encoders { - if let property = self.keyValuePairs?[typeName] { + if let property = self.keyValuePairs[typeName] { try encoder(property, &additionalContainer) } } } /// It is possible to specify an arbitrary number of existing options as a list of key-value pairs, for example, `"key1": "value1"`, `"key2": "value2"`. - public var keyValuePairs: [String: Any]? = [:] + public var keyValuePairs: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return keyValuePairs[key] + } + set(newValue) { + keyValuePairs[key] = newValue + } + } enum CodingKeys: String, CodingKey { case keyValuePairs diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCurrentUser.swift b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUser.swift index 2a724f2..46ddeae 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yCurrentUser.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUser.swift @@ -41,6 +41,9 @@ public struct C8yCurrentUser: Codable { /// Indicates if the user should reset the password on the next login. public var shouldResetPassword: Bool? + /// Indicates if the user has to use two-factor authentication to log in. + public var twoFactorAuthenticationEnabled: Bool? + /// The user's username. It can have a maximum of 1000 characters. public var userName: String? @@ -59,6 +62,7 @@ public struct C8yCurrentUser: Codable { case phone case `self` = "self" case shouldResetPassword + case twoFactorAuthenticationEnabled case userName case devicePermissions } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpCode.swift b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpCode.swift new file mode 100644 index 0000000..1583470 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpCode.swift @@ -0,0 +1,23 @@ +// +// C8yCurrentUserTotpCode.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yCurrentUserTotpCode: Codable { + + /// Two-factor authentication code entered by the user to log in to the platform. + public var code: String? + + enum CodingKeys: String, CodingKey { + case code + } + + public init(code: String) { + self.code = code + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecret.swift b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecret.swift new file mode 100644 index 0000000..5626a9d --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecret.swift @@ -0,0 +1,26 @@ +// +// C8yCurrentUserTotpSecret.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yCurrentUserTotpSecret: Codable { + + /// Secret used by two-factor authentication applications to generate the TFA codes. + public var rawSecret: String? + + /// URL used to set the two-factor authentication secret for the TFA application. + public var secretQrUrl: String? + + enum CodingKeys: String, CodingKey { + case rawSecret + case secretQrUrl + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecretActivity.swift b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecretActivity.swift new file mode 100644 index 0000000..d1c529e --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yCurrentUserTotpSecretActivity.swift @@ -0,0 +1,23 @@ +// +// C8yCurrentUserTotpSecretActivity.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yCurrentUserTotpSecretActivity: Codable { + + /// Indicates whether the two-factor authentication secret is active. + public var isActive: Bool? + + enum CodingKeys: String, CodingKey { + case isActive + } + + public init(isActive: Bool) { + self.isActive = isActive + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift b/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift index cc8539d..5d978ae 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yCustomProperties.swift @@ -16,7 +16,7 @@ public struct C8yCustomProperties: Codable { self.language = try container.decodeIfPresent(String.self, forKey: .language) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yCustomProperties.decoders { - self.customProperties?[typeName] = try? decoder(additionalContainer) + self.customProperties[typeName] = try? decoder(additionalContainer) } } } @@ -26,7 +26,7 @@ public struct C8yCustomProperties: Codable { try container.encodeIfPresent(self.language, forKey: .language) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yCustomProperties.encoders { - if let property = self.customProperties?[typeName] { + if let property = self.customProperties[typeName] { try encoder(property, &additionalContainer) } } @@ -37,7 +37,16 @@ public struct C8yCustomProperties: Codable { /// It is possible to add an arbitrary number of custom properties as a list of key-value pairs, for example, `"property": "value"`. /// - public var customProperties: [String: Any]? = [:] + public var customProperties: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customProperties[key] + } + set(newValue) { + customProperties[key] = newValue + } + } enum CodingKeys: String, CodingKey { case language diff --git a/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift b/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift index 7e73ddc..d4e9f29 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yEvent.swift @@ -22,7 +22,7 @@ public struct C8yEvent: Codable { self.type = try container.decodeIfPresent(String.self, forKey: .type) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yEvent.decoders { - self.customFragments?[typeName] = try? decoder(additionalContainer) + self.customFragments[typeName] = try? decoder(additionalContainer) } } } @@ -39,7 +39,7 @@ public struct C8yEvent: Codable { try container.encodeIfPresent(self.type, forKey: .type) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yEvent.encoders { - if let property = self.customFragments?[typeName] { + if let property = self.customFragments[typeName] { try encoder(property, &additionalContainer) } } @@ -73,7 +73,16 @@ public struct C8yEvent: Codable { /// /// 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. /// - public var customFragments: [String: Any]? = [:] + public var customFragments: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case creationTime diff --git a/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift b/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift index 37674e1..13365b2 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yLoginForm.swift @@ -19,7 +19,7 @@ public struct C8yLoginForm: Codable { /// Used in cases of basic or OAI-Secure authentication. public var password: String? - /// Used if TFA code is required. + /// Current TFA code, sent by the user, if a TFA code is required to log in. public var tfaCode: String? /// Used in cases of basic or OAI-Secure authentication. diff --git a/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift b/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift index 0e63151..d2d3d03 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yManagedObject.swift @@ -30,7 +30,7 @@ public struct C8yManagedObject: Codable { self.c8ySupportedOperations = try container.decodeIfPresent([String].self, forKey: .c8ySupportedOperations) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yManagedObject.decoders { - self.customFragments?[typeName] = try? decoder(additionalContainer) + self.customFragments[typeName] = try? decoder(additionalContainer) } } } @@ -55,7 +55,7 @@ public struct C8yManagedObject: Codable { try container.encodeIfPresent(self.c8ySupportedOperations, forKey: .c8ySupportedOperations) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yManagedObject.encoders { - if let property = self.customFragments?[typeName] { + if let property = self.customFragments[typeName] { try encoder(property, &additionalContainer) } } @@ -113,7 +113,16 @@ public struct C8yManagedObject: Codable { /// /// 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. /// - public var customFragments: [String: Any]? = [:] + public var customFragments: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case creationTime diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift b/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift index bdf23f7..20b8561 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMeasurement.swift @@ -20,7 +20,7 @@ public struct C8yMeasurement: Codable { self.c8ySteam = try container.decodeIfPresent(C8ySteam.self, forKey: .c8ySteam) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yMeasurement.decoders { - self.customFragments?[typeName] = try? decoder(additionalContainer) + self.customFragments[typeName] = try? decoder(additionalContainer) } } } @@ -35,7 +35,7 @@ public struct C8yMeasurement: Codable { try container.encodeIfPresent(self.c8ySteam, forKey: .c8ySteam) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yMeasurement.encoders { - if let property = self.customFragments?[typeName] { + if let property = self.customFragments[typeName] { try encoder(property, &additionalContainer) } } @@ -63,7 +63,16 @@ public struct C8yMeasurement: Codable { /// /// 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. /// - public var customFragments: [String: Any]? = [:] + public var customFragments: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case id diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMicroserviceApplicationManifest.swift b/Sources/CumulocityCoreLibrary/Model/C8yMicroserviceApplicationManifest.swift index 88d6bcc..aa07857 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMicroserviceApplicationManifest.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMicroserviceApplicationManifest.swift @@ -63,7 +63,7 @@ public struct C8yMicroserviceApplicationManifest: Codable { public var scale: C8yScale? /// A list of settings objects for this microservice application. - public var settings: [C8ySettings]? + public var settings: [C8yApplicationSettings]? /// Allows to specify a custom category for microservice settings. /// By default, `contextPath` is used. @@ -198,47 +198,4 @@ public struct C8yMicroserviceApplicationManifest: Codable { } } - - public struct C8ySettings: Codable { - - /// The name of the setting. - public var key: String? - - /// The value schema determines the values that the microservice can process. - public var valueSchema: C8yValueSchema? - - /// The default value. - public var defaultValue: String? - - /// Indicates if the value is editable. - public var editable: Bool? - - /// Indicated wether this setting is inherited. - public var inheritFromOwner: Bool? - - enum CodingKeys: String, CodingKey { - case key - case valueSchema - case defaultValue - case editable - case inheritFromOwner - } - - public init() { - } - - /// The value schema determines the values that the microservice can process. - public struct C8yValueSchema: Codable { - - /// The value schema type. - public var type: String? - - enum CodingKeys: String, CodingKey { - case type - } - - public init() { - } - } - } } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift b/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift index 511de46..3166d8a 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yMobile.swift @@ -22,7 +22,16 @@ public struct C8yMobile: Codable { /// Other possible values are: `c8y_Mobile.imsi`, `c8y_Mobile.currentOperator`, `c8y_Mobile.currentBand`, `c8y_Mobile.connType`, `c8y_Mobile.rssi`, `c8y_Mobile.ecn0`, `c8y_Mobile.rcsp`, `c8y_Mobile.mnc`, `c8y_Mobile.lac` and `c8y_Mobile.msisdn`. /// - public var customFragments: [String: String]? = [:] + public var customFragments: [String: String] = [:] + + subscript(key: String) -> String? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case imei diff --git a/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift b/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift index 325b5ac..28e3126 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yOperation.swift @@ -20,10 +20,9 @@ public struct C8yOperation: Codable { self.id = try container.decodeIfPresent(String.self, forKey: .id) self.`self` = try container.decodeIfPresent(String.self, forKey: .`self`) self.status = try container.decodeIfPresent(C8yStatus.self, forKey: .status) - self.comCumulocityModelWebCamDevice = try container.decodeIfPresent(C8yWebCamDevice.self, forKey: .comCumulocityModelWebCamDevice) if let additionalContainer = try? decoder.container(keyedBy: JSONCodingKeys.self) { for (typeName, decoder) in C8yOperation.decoders { - self.customFragments?[typeName] = try? decoder(additionalContainer) + self.customFragments[typeName] = try? decoder(additionalContainer) } } } @@ -38,10 +37,9 @@ public struct C8yOperation: Codable { try container.encodeIfPresent(self.id, forKey: .id) try container.encodeIfPresent(self.`self`, forKey: .`self`) try container.encodeIfPresent(self.status, forKey: .status) - try container.encodeIfPresent(self.comCumulocityModelWebCamDevice, forKey: .comCumulocityModelWebCamDevice) var additionalContainer = encoder.container(keyedBy: JSONCodingKeys.self) for (typeName, encoder) in C8yOperation.encoders { - if let property = self.customFragments?[typeName] { + if let property = self.customFragments[typeName] { try encoder(property, &additionalContainer) } } @@ -70,14 +68,20 @@ public struct C8yOperation: Codable { /// The status of the operation. public var status: C8yStatus? - /// Custom operation of a webcam. Note that this is an example for a custom object of the webcam operation. For other operations you can use other objects. - public var comCumulocityModelWebCamDevice: C8yWebCamDevice? - /// 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. /// - public var customFragments: [String: Any]? = [:] + public var customFragments: [String: Any] = [:] + + subscript(key: String) -> Any? { + get { + return customFragments[key] + } + set(newValue) { + customFragments[key] = newValue + } + } enum CodingKeys: String, CodingKey { case bulkOperationId @@ -88,7 +92,6 @@ public struct C8yOperation: Codable { case id case `self` = "self" case status - case comCumulocityModelWebCamDevice = "com_cumulocity_model_WebCamDevice" case customFragments } @@ -103,13 +106,6 @@ public struct C8yOperation: Codable { case pending = "PENDING" } - - /// Custom operation of a webcam. Note that this is an example for a custom object of the webcam operation. For other operations you can use other objects. - public struct C8yWebCamDevice: Codable { - - public init() { - } - } } extension C8yOperation { diff --git a/Sources/CumulocityCoreLibrary/Model/C8yPasswordChange.swift b/Sources/CumulocityCoreLibrary/Model/C8yPasswordChange.swift new file mode 100644 index 0000000..c08380c --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yPasswordChange.swift @@ -0,0 +1,28 @@ +// +// C8yPasswordChange.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yPasswordChange: Codable { + + /// The current password of the user performing the request. + public var currentUserPassword: String? + + /// The new password to be set for the user performing the request. + public var newPassword: String? + + enum CodingKeys: String, CodingKey { + case currentUserPassword + case newPassword + } + + public init(currentUserPassword: String, newPassword: String) { + self.currentUserPassword = currentUserPassword + self.newPassword = newPassword + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift b/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift index 6d749cd..54d5411 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yRequestRepresentation.swift @@ -57,7 +57,16 @@ public struct C8yRequestRepresentation: Codable { /// 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]? = [:] + public var requestHeaders: [String: String] = [:] + + subscript(key: String) -> String? { + get { + return requestHeaders[key] + } + set(newValue) { + requestHeaders[key] = newValue + } + } enum CodingKeys: String, CodingKey { case requestHeaders @@ -74,7 +83,16 @@ public struct C8yRequestRepresentation: Codable { /// 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]? = [:] + public var requestParameters: [String: String] = [:] + + subscript(key: String) -> String? { + get { + return requestParameters[key] + } + set(newValue) { + requestParameters[key] = newValue + } + } enum CodingKeys: String, CodingKey { case requestParameters diff --git a/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift b/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift index a7ab869..b106550 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8ySinglePhaseEnergyMeasurement.swift @@ -11,7 +11,16 @@ import Foundation /// Measurement of the single phase energy meter. public struct C8ySinglePhaseEnergyMeasurement: Codable { - public var additionalProperties: [String: C8yMeasurementValue]? = [:] + public var additionalProperties: [String: C8yMeasurementValue] = [:] + + subscript(key: String) -> C8yMeasurementValue? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } enum CodingKeys: String, CodingKey { case additionalProperties diff --git a/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaData.swift b/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaData.swift new file mode 100644 index 0000000..699ce5e --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yTenantTfaData.swift @@ -0,0 +1,49 @@ +// +// C8yTenantTfaData.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yTenantTfaData: Codable { + + /// Indicates whether two-factor authentication is enabled on system level or not. + public var enabledOnSystemLevel: Bool? + + /// Indicates whether two-factor authentication is enabled on tenant level or not. + public var enabledOnTenantLevel: Bool? + + /// Indicates whether two-factor authentication is enforced on system level or not. + public var enforcedOnSystemLevel: Bool? + + /// Two-factor authentication is enforced for the specified group. + public var enforcedUsersGroup: String? + + /// Two-factor authentication strategy. + public var strategy: C8yStrategy? + + /// Indicates whether two-factor authentication is enforced on tenant level or not. + public var totpEnforcedOnTenantLevel: Bool? + + enum CodingKeys: String, CodingKey { + case enabledOnSystemLevel + case enabledOnTenantLevel + case enforcedOnSystemLevel + case enforcedUsersGroup + case strategy + case totpEnforcedOnTenantLevel + } + + public init() { + } + + /// 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 6f173cd..cb498f1 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yThreePhaseEnergyMeasurement.swift @@ -11,7 +11,16 @@ import Foundation /// Measurement of the three phase energy meter. public struct C8yThreePhaseEnergyMeasurement: Codable { - public var additionalProperties: [String: C8yMeasurementValue]? = [:] + public var additionalProperties: [String: C8yMeasurementValue] = [:] + + subscript(key: String) -> C8yMeasurementValue? { + get { + return additionalProperties[key] + } + set(newValue) { + additionalProperties[key] = newValue + } + } enum CodingKeys: String, CodingKey { case additionalProperties diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift new file mode 100644 index 0000000..8c6aea1 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yUploadedTrustedCertSignedVerificationCode.swift @@ -0,0 +1,23 @@ +// +// C8yUploadedTrustedCertSignedVerificationCode.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 signed verification code to prove the user's possession of the certificate. +public struct C8yUploadedTrustedCertSignedVerificationCode: Codable { + + /// A signed verification code that proves the right to use the certificate. + public var proofOfPossessionSignedVerificationCode: String? + + enum CodingKeys: String, CodingKey { + case proofOfPossessionSignedVerificationCode + } + + public init() { + } +} diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUser.swift b/Sources/CumulocityCoreLibrary/Model/C8yUser.swift index 96e3e19..b16e6a1 100644 --- a/Sources/CumulocityCoreLibrary/Model/C8yUser.swift +++ b/Sources/CumulocityCoreLibrary/Model/C8yUser.swift @@ -70,6 +70,9 @@ public struct C8yUser: Codable { /// Indicates if the user should reset the password on the next login. public var shouldResetPassword: Bool? + /// Indicates if the user has to use two-factor authentication to log in. + public var twoFactorAuthenticationEnabled: Bool? + /// The user's username. It can have a maximum of 1000 characters. public var userName: String? @@ -96,6 +99,7 @@ public struct C8yUser: Codable { case `self` = "self" case sendPasswordResetEmail case shouldResetPassword + case twoFactorAuthenticationEnabled case userName case devicePermissions } diff --git a/Sources/CumulocityCoreLibrary/Model/C8yUserTfaData.swift b/Sources/CumulocityCoreLibrary/Model/C8yUserTfaData.swift new file mode 100644 index 0000000..84750f9 --- /dev/null +++ b/Sources/CumulocityCoreLibrary/Model/C8yUserTfaData.swift @@ -0,0 +1,41 @@ +// +// C8yUserTfaData.swift +// CumulocityCoreLibrary +// +// Copyright (c) 2014-2022 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 C8yUserTfaData: Codable { + + /// Latest date and time when the user has used two-factor authentication to log in. + public var lastTfaRequestTime: String? + + /// Two-factor authentication strategy. + public var strategy: C8yStrategy? + + /// Indicates whether the user has enabled two-factor authentication or not. + public var tfaEnabled: Bool? + + /// Indicates whether two-factor authentication is enforced by the tenant admin or not. + public var tfaEnforced: Bool? + + enum CodingKeys: String, CodingKey { + case lastTfaRequestTime + case strategy + case tfaEnabled + case tfaEnforced + } + + public init() { + } + + /// Two-factor authentication strategy. + public enum C8yStrategy: String, Codable { + case sms = "SMS" + case totp = "TOTP" + } + +} diff --git a/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift b/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift index 7002658..16e9b2e 100644 --- a/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift +++ b/Sources/CumulocityCoreLibrary/Supplementary/CumulocityCoreLibrary.swift @@ -193,7 +193,6 @@ extension Cumulocity { private var factory: Core - public lazy var inventoryApi: InventoryApi = InventoryApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var managedObjectsApi: ManagedObjectsApi = ManagedObjectsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var binariesApi: BinariesApi = BinariesApi(requestBuilder: factory.requestBuilder, withSession: factory.session) public lazy var childOperationsApi: ChildOperationsApi = ChildOperationsApi(requestBuilder: factory.requestBuilder, withSession: factory.session) diff --git a/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift b/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift index e954d2d..9f14d23 100644 --- a/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift +++ b/Sources/CumulocityCoreLibrary/Supplementary/URLRequestBuilder.swift @@ -68,7 +68,7 @@ public class URLRequestBuilder { return self } - public func set(httpBody: Data) -> URLRequestBuilder { + public func set(httpBody: Data?) -> URLRequestBuilder { self.httpBody = httpBody return self } diff --git a/Tests/CumulocityCoreLibraryTests/Api/CurrentUserApiTest.swift b/Tests/CumulocityCoreLibraryTests/Api/CurrentUserApiTest.swift index 378a237..35f7619 100644 --- a/Tests/CumulocityCoreLibraryTests/Api/CurrentUserApiTest.swift +++ b/Tests/CumulocityCoreLibraryTests/Api/CurrentUserApiTest.swift @@ -39,4 +39,17 @@ public class CurrentUserApiTest: XCTestCase { }).store(in: &cancellables) wait(for: [expectation], timeout: 10) } + + public func testGetTfaState() { + let expectation = XCTestExpectation(description: "ok") + var cancellables = Set() + try? TestableCurrentUserApi().getTfaState().sink(receiveCompletion: { completion in + let message = try? completion.error() + print(message?.statusCode ?? "Successfully") + }, receiveValue: { data in + expectation.fulfill() + print(data) + }).store(in: &cancellables) + wait(for: [expectation], timeout: 10) + } } diff --git a/Tests/CumulocityCoreLibraryTests/Api/InventoryApiTest.swift b/Tests/CumulocityCoreLibraryTests/Api/InventoryApiTest.swift deleted file mode 100644 index 5da19bf..0000000 --- a/Tests/CumulocityCoreLibraryTests/Api/InventoryApiTest.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// InventoryApiTest.swift -// CumulocityCoreLibrary -// -// Copyright (c) 2014-2022 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 Combine -import XCTest - -@testable import CumulocityCoreLibrary - -public class InventoryApiTest: XCTestCase { - - class TestableInventoryApi: InventoryApi { - - override func adapt(builder: URLRequestBuilder) -> URLRequestBuilder { - guard let testDataUrl = Bundle.module.path(forResource: "TestData", ofType: "plist") else { return builder } - let resources = NSDictionary(contentsOfFile: testDataUrl) - let scheme = resources?["Scheme"] as? String ?? "" - let hostName = resources?["HostName"] as? String ?? "" - let authorization = resources?["Authorization"] as? String ?? "" - return builder.set(scheme: scheme) - .set(host: hostName) - .add(header: "Authorization", value: authorization) - } - } - - public func testGetInventoryApiResource() { - let expectation = XCTestExpectation(description: "ok") - var cancellables = Set() - try? TestableInventoryApi().getInventoryApiResource().sink(receiveCompletion: { completion in - let message = try? completion.error() - print(message?.statusCode ?? "Successfully") - }, receiveValue: { data in - expectation.fulfill() - print(data) - }).store(in: &cancellables) - wait(for: [expectation], timeout: 10) - } -}