sdk/communication/AzureCommunicationChat/Source/Registrar/RegistrarClient.swift (149 lines of code) (raw):

// -------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // The MIT License (MIT) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the ""Software""), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // // -------------------------------------------------------------------------- import AzureCommunicationCommon import AzureCore import Foundation internal class RegistrarClient: PipelineClient { // MARK: Properties /// Registrar URL. private let url: URL /// CommunicationTokenCredential for authenticating requests. private let credential: CommunicationTokenCredential /// Unique identifier for the registration. private let registrationId: String /// URL Session. private let session: URLSession /// Options provided to configure this RegistrarClient internal let options: ClientOptions // MARK: Initializers internal init( endpoint: String, credential: CommunicationTokenCredential, registrationId: String, sessionConfiguration: URLSessionConfiguration? = nil, headersPolicy: HeadersPolicy, withOptions options: ClientOptions ) throws { guard let registrarUrl = URL(string: endpoint) else { throw AzureError.client("Unable to form base registrar URL.") } self.url = registrarUrl self.credential = credential self.registrationId = registrationId self.session = URLSession(configuration: sessionConfiguration ?? .default) self.options = options super.init( endpoint: registrarUrl, transport: options.transportOptions.transport ?? URLSessionTransport(), policies: [ UserAgentPolicy(for: RegistrarClient.self, telemetryOptions: options.telemetryOptions), RequestIdPolicy(), AddDatePolicy(), headersPolicy, LoggingPolicy() ], logger: options.logger, options: options ) } /// Sends an HTTP request to Registrar. /// - Parameters: /// - request: The HTTP request. /// - completionHandler: Returns the URLResponse, and an error if any errors occurred. private func sendHttpRequest( _ request: HTTPRequest, completionHandler: @escaping (Result<HTTPResponse?, AzureError>) -> Void ) { let dispatchQueue = DispatchQueue.main let context = PipelineContext.of(keyValues: [ ContextKey.allowedStatusCodes.rawValue: [202] as AnyObject ]) self.request(request, context: context) { result, httpResponse in switch result { case .success: dispatchQueue.async { completionHandler(.success(httpResponse)) } case let .failure(error): dispatchQueue.async { completionHandler(.failure(AzureError.client("Registration request failed.", error))) } } } } // MARK: Internal Methods /// Sets a registration in Registrar. /// - Parameters: /// - clientDescription: RegistrarClientDescription. /// - transports: RegistrarTransports, a transport contains the APNS token as the path. /// - completionHandler: Returns the response. Success indicates the registration was received. internal func setRegistration( with clientDescription: RegistrarClientDescription, for registrarTransportSettings: [RegistrarTransportSettings], completionHandler: @escaping (Result<HTTPResponse?, AzureError>) -> Void ) { let registrarRequestBody = RegistrarRequestBody( registrationId: registrationId, clientDescription: clientDescription, registrarTransportSettings: [ "APNS": registrarTransportSettings ] ) guard let data = try? JSONEncoder().encode(registrarRequestBody) else { completionHandler(.failure(AzureError.client("Failed to serialize POST request body."))) return } guard let request = try? HTTPRequest(method: HTTPMethod.post, url: url, data: data) else { completionHandler(.failure(AzureError.client("Failed to create POST request in registration process."))) return } sendHttpRequest(request) { result in switch result { case let .success(response): completionHandler(.success(response)) case let .failure(error): completionHandler(.failure(AzureError.client("Registration request failed.", error))) } } } /// Deletes a registration in Registrar. /// - Parameters: /// - completionHandler: Returns the response. Success indicates the registration was deleted. internal func deleteRegistration( completionHandler: @escaping (Result<HTTPResponse?, AzureError>) -> Void ) { let url = self.url.appendingPathComponent("/\(registrationId)") guard let request = try? HTTPRequest(method: HTTPMethod.delete, url: url) else { completionHandler(.failure(AzureError.client("Failed to create DELETE request in registration process."))) return } sendHttpRequest(request) { result in switch result { case let .success(response): completionHandler(.success(response)) case let .failure(error): completionHandler(.failure(AzureError.client("Registration request failed.", error))) } } } } internal func createRegistrarClient( credential: CommunicationTokenCredential, options: AzureCommunicationChatClientOptions, registrationId: String, completionHandler: @escaping (Result<RegistrarClient, AzureError>) -> Void ) { // Internal options do not use the CommunicationSignalingErrorHandler let internalOptions = AzureCommunicationChatClientOptionsInternal( apiVersion: AzureCommunicationChatClientOptionsInternal.ApiVersion(options.apiVersion), logger: options.logger, telemetryOptions: options.telemetryOptions, transportOptions: options.transportOptions, dispatchQueue: options.dispatchQueue ) // Get skypeToken from CommunicationTokenCredential credential.token { accessToken, _ in guard let token = accessToken?.token else { completionHandler( .failure(AzureError.client("Failed to get token from CommunicationTokenCredential.")) ) return } // Use skypeToken to set authentication header and create headersPolicy let httpHeaders = [ RegistrarHeader.contentType.rawValue: RegistrarMimeType.json.rawValue, RegistrarHeader.skypeTokenHeader.rawValue: token ] let headersPolicy = HeadersPolicy(addingHeaders: httpHeaders) // Use skypeToken to get the Registrar Service Url guard let registrarServiceUrl = try? getRegistrarServiceUrl(token: token) else { completionHandler(.failure(AzureError.client("Failed to get Registrar Service URL."))) return } // Initialize the RegistrarClient guard let registrarClient = try? RegistrarClient( endpoint: registrarServiceUrl, credential: credential, registrationId: registrationId, headersPolicy: headersPolicy, withOptions: internalOptions ) else { completionHandler(.failure(AzureError.client("Failed to initialize the RegistrarClient."))) return } completionHandler(.success(registrarClient)) } }