From aea5f7518b44ddaf89c14da74b9a126638ca3316 Mon Sep 17 00:00:00 2001 From: Hector Rondon Date: Fri, 20 Oct 2017 18:15:35 -0400 Subject: [PATCH] feat(api): add basic endpoints (#9) --- .gitignore | 1 + Cartfile | 1 + Cartfile.resolved | 1 + Example/.swiftlint.yml | 8 + Example/Source/AppDelegate.swift | 4 - Example/Source/ViewController.swift | 11 +- Glpi.xcodeproj/project.pbxproj | 48 +++++ Source/GlpiRequest.swift | 266 ++++++++++++++++++++++++++++ Source/Routers.swift | 154 ++++++++++++++++ ci/scripts/install.sh | 2 + circle.yml | 1 + 11 files changed, 489 insertions(+), 8 deletions(-) create mode 100644 Cartfile create mode 100644 Cartfile.resolved create mode 100644 Example/.swiftlint.yml create mode 100644 Source/GlpiRequest.swift create mode 100644 Source/Routers.swift diff --git a/.gitignore b/.gitignore index d861096..8c1125b 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ playground.xcworkspace # Carthage/Checkouts Carthage/Build +Carthage/Checkouts # fastlane # diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..8c5b013 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "Alamofire/Alamofire" \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..a8c2ced --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "Alamofire/Alamofire" "4.5.1" diff --git a/Example/.swiftlint.yml b/Example/.swiftlint.yml new file mode 100644 index 0000000..f82bcce --- /dev/null +++ b/Example/.swiftlint.yml @@ -0,0 +1,8 @@ +disabled_rules: # rule identifiers to exclude from running + - function_body_length + - line_length + - identifier_name + - empty_enum_arguments + - trailing_whitespace +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Pods diff --git a/Example/Source/AppDelegate.swift b/Example/Source/AppDelegate.swift index 63365f6..1b52ecd 100644 --- a/Example/Source/AppDelegate.swift +++ b/Example/Source/AppDelegate.swift @@ -34,7 +34,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) @@ -65,7 +64,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } - - } - diff --git a/Example/Source/ViewController.swift b/Example/Source/ViewController.swift index f82e6da..1ac933d 100644 --- a/Example/Source/ViewController.swift +++ b/Example/Source/ViewController.swift @@ -33,15 +33,18 @@ import Glpi class ViewController: UIViewController { override func viewDidLoad() { + + GlpiRequest.initSession(userToken: "user_token") { result in + if let data = result { + print(data) + } + } + super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } - - } - diff --git a/Glpi.xcodeproj/project.pbxproj b/Glpi.xcodeproj/project.pbxproj index 7adc820..d589b96 100644 --- a/Glpi.xcodeproj/project.pbxproj +++ b/Glpi.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 0A06BB211F913C2C001D53B5 /* Glpi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A06BB171F913C2C001D53B5 /* Glpi.framework */; }; 0A06BB261F913C2C001D53B5 /* GlpiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A06BB251F913C2C001D53B5 /* GlpiTests.swift */; }; 0A06BB281F913C2C001D53B5 /* Glpi.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A06BB1A1F913C2C001D53B5 /* Glpi.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0A1CF5AC1F97ADF90048E866 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A1CF5AB1F97ADF90048E866 /* Alamofire.framework */; }; + 0A1CF5B31F97B3D00048E866 /* Routers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1CF5B21F97B3D00048E866 /* Routers.swift */; }; + 0A1CF5B61F97BBF60048E866 /* GlpiRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1CF5B51F97BBF60048E866 /* GlpiRequest.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -30,6 +33,9 @@ 0A06BB251F913C2C001D53B5 /* GlpiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlpiTests.swift; sourceTree = ""; }; 0A06BB271F913C2C001D53B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0A06BB361F91401D001D53B5 /* Glpi.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Glpi.podspec; sourceTree = ""; }; + 0A1CF5AB1F97ADF90048E866 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; + 0A1CF5B21F97B3D00048E866 /* Routers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routers.swift; sourceTree = ""; }; + 0A1CF5B51F97BBF60048E866 /* GlpiRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlpiRequest.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -37,6 +43,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0A1CF5AC1F97ADF90048E866 /* Alamofire.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -59,6 +66,7 @@ 0A06BB191F913C2C001D53B5 /* Source */, 0A06BB241F913C2C001D53B5 /* Tests */, 0A06BB181F913C2C001D53B5 /* Products */, + 0A1CF5AA1F97ADF90048E866 /* Frameworks */, ); sourceTree = ""; }; @@ -121,9 +129,19 @@ name = Documentation; sourceTree = ""; }; + 0A1CF5AA1F97ADF90048E866 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0A1CF5AB1F97ADF90048E866 /* Alamofire.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 0A87A3151F916A000080622B /* Core */ = { isa = PBXGroup; children = ( + 0A1CF5B21F97B3D00048E866 /* Routers.swift */, + 0A1CF5B51F97BBF60048E866 /* GlpiRequest.swift */, ); name = Core; sourceTree = ""; @@ -150,6 +168,7 @@ 0A06BB131F913C2C001D53B5 /* Frameworks */, 0A06BB141F913C2C001D53B5 /* Headers */, 0A06BB151F913C2C001D53B5 /* Resources */, + 0A1CF5AE1F97AE440048E866 /* ShellScript */, ); buildRules = ( ); @@ -234,11 +253,30 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 0A1CF5AE1F97AE440048E866 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 0A06BB121F913C2C001D53B5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0A1CF5B31F97B3D00048E866 /* Routers.swift in Sources */, + 0A1CF5B61F97BBF60048E866 /* GlpiRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -385,6 +423,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.3; @@ -409,6 +451,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.3; @@ -424,6 +470,7 @@ 0A06BB2F1F913C2C001D53B5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = H7XJV96LX2; INFOPLIST_FILE = Tests/Info.plist; @@ -438,6 +485,7 @@ 0A06BB301F913C2C001D53B5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = H7XJV96LX2; INFOPLIST_FILE = Tests/Info.plist; diff --git a/Source/GlpiRequest.swift b/Source/GlpiRequest.swift new file mode 100644 index 0000000..33ea8c2 --- /dev/null +++ b/Source/GlpiRequest.swift @@ -0,0 +1,266 @@ +// +/* + * Copyright © 2017 Teclib. All rights reserved. + * + * Request.swift is part of Glpi + * + * Glpi is a subproject of Flyve MDM. Flyve MDM is a mobile + * device management software. + * + * Glpi is Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ------------------------------------------------------------------------------ + * @author Hector Rondon + * @date 18/10/17 + * @copyright Copyright © 2017 Teclib. All rights reserved. + * @license Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 + * @link https://github.com/flyve-mdm/[name] + * @link https://flyve-mdm.com + * ------------------------------------------------------------------------------ + */ + + +import Foundation +import Alamofire + +/// Session Token +public var SESSION_TOKEN = String() + +public class GlpiRequest { + + /** + Request a session token to uses other api endpoints. + - parameter: user token + - parameter: app token (optional) + */ + class public func initSession(userToken: String, appToken: String = "", completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.initSession(userToken, appToken)) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + do { + if let data = (result as AnyObject).data(using: String.Encoding.utf8.rawValue) { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { + + if let session_token = json["session_token"] { + SESSION_TOKEN = session_token + } + completion(result) + } + } + } catch { + completion(GlpiRequest.handlerError(response)) + } + case .failure(_ ): + SESSION_TOKEN = "" + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request a session token to uses other api endpoints. + - parameter: user + - parameter: password + - parameter: app token (optional) + */ + class public func initSession(user: String, password: String, appToken: String = "", completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.initSessionByBasicAuth(user, password, appToken)) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + do { + if let data = (result as AnyObject).data(using: String.Encoding.utf8.rawValue) { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { + + if let session_token = json["session_token"] { + SESSION_TOKEN = session_token + } + completion(result) + } + } + } catch { + completion(GlpiRequest.handlerError(response)) + } + case .failure(_ ): + SESSION_TOKEN = "" + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request kill current session + */ + class public func killSession(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.killSession) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + SESSION_TOKEN = "" + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get my profiles + */ + class public func getMyProfiles(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getMyProfiles) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get active profile + */ + class public func getActiveProfile(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getActiveProfile) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get my entities + */ + class public func getMyEntities(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getMyEntities) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get active entities + */ + class public func getActiveEntities(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getActiveEntities) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get full session information + */ + class public func getFullSession(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getFullSession) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get Glpi Configuration + */ + class public func getGlpiConfig(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getGlpiConfig) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + /** + Request get multiple items + */ + class public func getMultipleItems(completion: @escaping (_ result: Any?) -> Void) { + + Alamofire.request(Routers.getMultipleItems) + .validate() + .responseJSON { response in + switch response.result { + case .success(let result): + completion(result) + case .failure(_ ): + completion(GlpiRequest.handlerError(response)) + } + } + } + + + /** + handler Error + - return: error message + */ + class func handlerError(_ response: DataResponse) -> [String: String] { + + var errorObj = [String]() + var errorDict = [String: String]() + + if let data = response.data { + errorObj = try! JSONSerialization.jsonObject(with: data) as? [String] ?? [String]() + } + + if errorObj.count == 2 { + errorDict["error"] = errorObj[0] + errorDict["message"] = errorObj[1] + } else { + errorDict["error"] = "" + errorDict["message"] = "" + } + + return errorDict + } +} diff --git a/Source/Routers.swift b/Source/Routers.swift new file mode 100644 index 0000000..6b8a2e5 --- /dev/null +++ b/Source/Routers.swift @@ -0,0 +1,154 @@ +// +/* + * Copyright © 2017 Teclib. All rights reserved. + * + * Routers.swift is part of Glpi + * + * Glpi is a subproject of Flyve MDM. Flyve MDM is a mobile + * device management software. + * + * Glpi is Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ------------------------------------------------------------------------------ + * @author Hector Rondon + * @date 18/10/17 + * @copyright Copyright © 2017 Teclib. All rights reserved. + * @license Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 + * @link https://github.com/flyve-mdm/[name] + * @link https://flyve-mdm.com + * ------------------------------------------------------------------------------ + */ + + +import Foundation +import Alamofire + +/// Enumerate endpoints methods +public enum Routers: URLRequestConvertible { + + /// GET /initSession + case initSession(String, String) + /// GET /initSession + case initSessionByBasicAuth(String, String, String) + /// GET /killSession + case killSession + /// GET /getMyProfiles + case getMyProfiles + /// GET /getActiveProfile + case getActiveProfile + /// GET /getMyEntities + case getMyEntities + /// GET /getActiveEntities + case getActiveEntities + /// GET /getFullSession + case getFullSession + /// GET /getGlpiConfig + case getGlpiConfig + /// GET /getMultipleItems + case getMultipleItems + + /// get HTTP Method + var method: Alamofire.HTTPMethod { + switch self { + case .initSession, .initSessionByBasicAuth, .killSession, .getMyProfiles, .getActiveProfile, + .getMyEntities, .getActiveEntities, .getFullSession, .getGlpiConfig, .getMultipleItems: + return .get + } + } + + /// build up and return the URL for each endpoint + var path: String { + + switch self { + case .initSession, .initSessionByBasicAuth: + return "/initSession" + case .killSession: + return "/killSession" + case .getMyProfiles: + return "/getMyProfiles" + case .getActiveProfile: + return "/getActiveProfile" + case .getMyEntities: + return "/getMyEntities" + case .getActiveEntities: + return "/getActiveEntities" + case .getFullSession: + return "/getFullSession" + case .getGlpiConfig: + return "/getGlpiConfig" + case .getMultipleItems: + return "/getMultipleItems" + } + } + + /// build up and return the query for each endpoint + var query: String { + + switch self { + case .initSession, .initSessionByBasicAuth, .killSession, .getMyProfiles, .getActiveProfile, + .getMyEntities, .getActiveEntities, .getFullSession, .getGlpiConfig, .getMultipleItems: + return "" + } + } + + /// build up and return the header for each endpoint + var header: [String: String] { + + var dictHeader = [String: String]() + dictHeader["Content-Type"] = "application/json" + + switch self { + case .initSession(let userToken, let appToken) : + + dictHeader["Authorization"] = "user_token \(userToken)" + + if !appToken.isEmpty { + dictHeader["App-Token"] = appToken + } + return dictHeader + case .initSessionByBasicAuth(let user, let password, let appToken): + let credentialData = "\(user):\(password)".data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))! + let base64Credentials = credentialData.base64EncodedString() + + dictHeader["Authorization"] = "Basic \(base64Credentials)" + + if !appToken.isEmpty { + dictHeader["App-Token"] = appToken + } + return dictHeader + default: + + dictHeader["Session-Token"] = "user_token \(SESSION_TOKEN)" + return dictHeader + } + } + + /** + Returns a URL request or throws if an `Error` was encountered + + - throws: An `Error` if the underlying `URLRequest` is `nil`. + - returns: A URL request. + */ + public func asURLRequest() throws -> URLRequest { + + let url = URL(string: "https://dev.flyve.org/glpi/apirest.php")! + var urlRequest = URLRequest(url: url.appendingPathComponent(path)) + + urlRequest.httpMethod = method.rawValue + + for (key, value) in header { + urlRequest.setValue(value, forHTTPHeaderField: key) + } + + return urlRequest + } +} diff --git a/ci/scripts/install.sh b/ci/scripts/install.sh index b6616a3..41fbe48 100755 --- a/ci/scripts/install.sh +++ b/ci/scripts/install.sh @@ -39,6 +39,8 @@ gem install bundler brew install node # Install jq for json parse brew install jq +# Install carthage +brew install carthage # Install standard-version scope global npm i -g standard-version # Install conventional-github-releaser scope global diff --git a/circle.yml b/circle.yml index 8920cdc..023f521 100644 --- a/circle.yml +++ b/circle.yml @@ -16,6 +16,7 @@ dependencies: - source "${SCRIPT_PATH}/bundler_config.sh" override: - source "${SCRIPT_PATH}/install.sh" + - carthage update test: pre: - source "${SCRIPT_PATH}/fastlane_config.sh"