Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin login API #89

Merged
merged 2 commits into from Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 56 additions & 0 deletions OmiseGO.xcodeproj/project.pbxproj
Expand Up @@ -155,6 +155,12 @@
0379E82221184CE900E65D4F /* CredentialEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0379E82121184CE900E65D4F /* CredentialEncoder.swift */; };
0380AAE9212D2D78006B2193 /* SignupParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0380AAE8212D2D78006B2193 /* SignupParams.swift */; };
0380AAEB212D311B006B2193 /* SignupFixtureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0380AAEA212D311B006B2193 /* SignupFixtureTests.swift */; };
03AB7ED3214BC04D00C80D8E /* AdminCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB7ECA214BC04C00C80D8E /* AdminCredentialTests.swift */; };
03AB7ED4214BC04D00C80D8E /* APIAdminndpointTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB7ECB214BC04C00C80D8E /* APIAdminndpointTest.swift */; };
03AB7ED5214BC04D00C80D8E /* FixtureAdminTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB7ECD214BC04C00C80D8E /* FixtureAdminTestCase.swift */; };
03AB7ED7214BC04D00C80D8E /* FixtureAdminAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB7ED1214BC04C00C80D8E /* FixtureAdminAPI.swift */; };
03AB7ED8214BC04D00C80D8E /* LoginAdminFixtureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB7ED2214BC04C00C80D8E /* LoginAdminFixtureTests.swift */; };
03AB7EDA214BC3F000C80D8E /* admin_fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 03AB7ED9214BC3F000C80D8E /* admin_fixtures */; };
03BCCCB31F8B311600F604DB /* OmiseGO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03BCCCA91F8B311600F604DB /* OmiseGO.framework */; };
03E565B3211AC87500BC9124 /* SocketClient+Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E565B2211AC87500BC9124 /* SocketClient+Client.swift */; };
03E565B5211AFBB100BC9124 /* QRScannerViewController+Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E565B4211AFBB100BC9124 /* QRScannerViewController+Client.swift */; };
Expand Down Expand Up @@ -329,6 +335,12 @@
0379E82C2118527C00E65D4F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0380AAE8212D2D78006B2193 /* SignupParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupParams.swift; sourceTree = "<group>"; };
0380AAEA212D311B006B2193 /* SignupFixtureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupFixtureTests.swift; sourceTree = "<group>"; };
03AB7ECA214BC04C00C80D8E /* AdminCredentialTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdminCredentialTests.swift; sourceTree = "<group>"; };
03AB7ECB214BC04C00C80D8E /* APIAdminndpointTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIAdminndpointTest.swift; sourceTree = "<group>"; };
03AB7ECD214BC04C00C80D8E /* FixtureAdminTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FixtureAdminTestCase.swift; sourceTree = "<group>"; };
03AB7ED1214BC04C00C80D8E /* FixtureAdminAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FixtureAdminAPI.swift; sourceTree = "<group>"; };
03AB7ED2214BC04C00C80D8E /* LoginAdminFixtureTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginAdminFixtureTests.swift; sourceTree = "<group>"; };
03AB7ED9214BC3F000C80D8E /* admin_fixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = admin_fixtures; sourceTree = "<group>"; };
03BCCCA91F8B311600F604DB /* OmiseGO.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OmiseGO.framework; sourceTree = BUILT_PRODUCTS_DIR; };
03BCCCB21F8B311600F604DB /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
03E565B2211AC87500BC9124 /* SocketClient+Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SocketClient+Client.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -532,6 +544,10 @@
032CEDA7211D3D5300E44445 /* Admin */ = {
isa = PBXGroup;
children = (
03AB7EC9214BC04C00C80D8E /* APITests */,
03AB7ECC214BC04C00C80D8E /* FixtureTests */,
03AB7EC8214BC04C00C80D8E /* LiveTests */,
03AB7EC7214BC04C00C80D8E /* TestMocks */,
);
path = Admin;
sourceTree = "<group>";
Expand Down Expand Up @@ -764,6 +780,40 @@
path = API;
sourceTree = "<group>";
};
03AB7EC7214BC04C00C80D8E /* TestMocks */ = {
isa = PBXGroup;
children = (
);
path = TestMocks;
sourceTree = "<group>";
};
03AB7EC8214BC04C00C80D8E /* LiveTests */ = {
isa = PBXGroup;
children = (
);
path = LiveTests;
sourceTree = "<group>";
};
03AB7EC9214BC04C00C80D8E /* APITests */ = {
isa = PBXGroup;
children = (
03AB7ECA214BC04C00C80D8E /* AdminCredentialTests.swift */,
03AB7ECB214BC04C00C80D8E /* APIAdminndpointTest.swift */,
);
path = APITests;
sourceTree = "<group>";
};
03AB7ECC214BC04C00C80D8E /* FixtureTests */ = {
isa = PBXGroup;
children = (
03AB7ED9214BC3F000C80D8E /* admin_fixtures */,
03AB7ECD214BC04C00C80D8E /* FixtureAdminTestCase.swift */,
03AB7ED1214BC04C00C80D8E /* FixtureAdminAPI.swift */,
03AB7ED2214BC04C00C80D8E /* LoginAdminFixtureTests.swift */,
);
path = FixtureTests;
sourceTree = "<group>";
};
03BCCC9F1F8B311600F604DB = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -917,6 +967,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
03AB7EDA214BC3F000C80D8E /* admin_fixtures in Resources */,
032CEE46211D3F2700E44445 /* core_fixtures in Resources */,
032CEF24211D685600E44445 /* secret.plist in Resources */,
032CEE48211D3F5100E44445 /* client_fixtures in Resources */,
Expand Down Expand Up @@ -1090,12 +1141,14 @@
032CEDEB211D3D5300E44445 /* TestObject.swift in Sources */,
032CEE1F211D3D5300E44445 /* ListableTests.swift in Sources */,
032CEE40211D3D5300E44445 /* TransactionLiveTests.swift in Sources */,
03AB7ED4214BC04D00C80D8E /* APIAdminndpointTest.swift in Sources */,
032CEDDC211D3D5300E44445 /* APIErrorTests.swift in Sources */,
032CEE3C211D3D5300E44445 /* AuthenticationLiveTests.swift in Sources */,
032CEE20211D3D5300E44445 /* FixtureClient.swift in Sources */,
032CEDFB211D3D5300E44445 /* DecodingFixtureTests.swift in Sources */,
032CEDDA211D3D5300E44445 /* EncodeTests.swift in Sources */,
032CEDE6211D3D5300E44445 /* TestQRHelper.swift in Sources */,
03AB7ED7214BC04D00C80D8E /* FixtureAdminAPI.swift in Sources */,
03E72EDF2124312C0060E1D7 /* AuthenticationTokenTests.swift in Sources */,
032CEDD2211D3D5300E44445 /* QRScannerViewModelTests.swift in Sources */,
032CEE39211D3D5300E44445 /* TransactionFixtureTests.swift in Sources */,
Expand All @@ -1108,6 +1161,7 @@
032CEDD6211D3D5300E44445 /* FixtureSocketClient.swift in Sources */,
032CEE3E211D3D5300E44445 /* SettingLiveTests.swift in Sources */,
032CEDE5211D3D5300E44445 /* TestSocketEventDelegate.swift in Sources */,
03AB7ED8214BC04D00C80D8E /* LoginAdminFixtureTests.swift in Sources */,
032CEE23211D3D5300E44445 /* OMGNumberFormatterTests.swift in Sources */,
032CEE3A211D3D5300E44445 /* SettingFixtureTests.swift in Sources */,
032CEDF0211D3D5300E44445 /* TransactionConsumptionParamsTest.swift in Sources */,
Expand All @@ -1117,6 +1171,8 @@
032CEE3D211D3D5300E44445 /* WalletLiveTests.swift in Sources */,
032CEE29211D3D5300E44445 /* UserFixtureTests.swift in Sources */,
032CEE26211D3D5300E44445 /* ClientCredentialTests.swift in Sources */,
03AB7ED3214BC04D00C80D8E /* AdminCredentialTests.swift in Sources */,
03AB7ED5214BC04D00C80D8E /* FixtureAdminTestCase.swift in Sources */,
032CEDD0211D3D5300E44445 /* QRScannerViewTests.swift in Sources */,
032CEDD4211D3D5300E44445 /* QRReaderTests.swift in Sources */,
032CEDD7211D3D5300E44445 /* SocketClientTests.swift in Sources */,
Expand Down
12 changes: 10 additions & 2 deletions Source/Admin/API/APIAdminEndpoint.swift
Expand Up @@ -8,11 +8,19 @@

/// Represents an admin api endpoint.
enum APIAdminEndpoint: APIEndpoint {
case login(params: LoginParams)

var path: String {
return ""
switch self {
case .login:
return "/admin.login"
}
}

var task: HTTPTask {
return .requestPlain
switch self {
case let .login(parameters):
return .requestParameters(parameters: parameters)
}
}
}
2 changes: 1 addition & 1 deletion Source/Admin/API/AdminConfiguration.swift
Expand Up @@ -27,7 +27,7 @@ public struct AdminConfiguration: Configuration {
/// - apiKey: The API key (typically generated on the admin panel)
/// - authenticationToken: The authentication token of the current user
/// - debugLog: Enable or not SDK console logs
public init(baseURL: String, credentials: AdminCredential, debugLog: Bool = false) {
public init(baseURL: String, credentials: AdminCredential = AdminCredential(), debugLog: Bool = false) {
self.baseURL = baseURL + "/api/admin"
self.credentials = credentials
self.debugLog = debugLog
Expand Down
23 changes: 18 additions & 5 deletions Source/Admin/API/AdminCredential.swift
Expand Up @@ -7,19 +7,32 @@
//

public struct AdminCredential: Credential {
public let userId: String
public var userId: String?
public var authenticationToken: String?

func isAuthenticated() -> Bool { return self.authenticationToken != nil }
/// Initialize an AdminCredential object with the given userId and authentication token
///
/// - Parameters:
/// - userId: The userId of the admin to use for the authenticated calls. Can be nil if doing request that don't need authentication.
/// - authenticationToken: The authentication token of the admin. Can be nil if doing request that don't need authentication.
public init(userId: String, authenticationToken: String) {
self.userId = userId
self.authenticationToken = authenticationToken
}

public init() {}

func isAuthenticated() -> Bool { return self.authenticationToken != nil && self.userId != nil }

func authentication() throws -> String? {
guard let authenticationToken = self.authenticationToken else {
throw OMGError.configuration(message: "Authentication token is required")
guard let authenticationToken = self.authenticationToken, let userId = self.userId else {
return nil
}
return try CredentialEncoder.encode(value1: self.userId, value2: authenticationToken, scheme: "OMGAdmin")
return try CredentialEncoder.encode(value1: userId, value2: authenticationToken, scheme: "OMGAdmin")
}

mutating func update(withAuthenticationToken authenticationToken: AuthenticationToken) {
self.userId = authenticationToken.user.id
self.authenticationToken = authenticationToken.token
}

Expand Down
35 changes: 31 additions & 4 deletions Source/Admin/API/HTTPAdminAPI.swift
Expand Up @@ -7,10 +7,37 @@
//

public class HTTPAdminAPI: HTTPAPI {
/// Initialize an http client using a configuration object that can be used to query admin endpoints
/// Initialize an http admin api client using a configuration object that can be used to query admin endpoints
///
/// - Parameter config: The ClientConfiguration object
public convenience init(config: AdminConfiguration) {
self.init(config: config)
/// - Parameter config: The AdminConfiguration object
public init(config: AdminConfiguration) {
super.init(config: config)
}
}

extension HTTPAdminAPI {
/// Login an admin using an email and a password.
/// Once the request is completed successfully, the current client is automatically
/// upgraded with the authentication contained in the response.
/// It can then be used to make other authenticated calls
///
/// - Parameters:
/// - params: The login params to use
/// - callback: The closure called when the request is completed
/// - Returns: An optional cancellable request.
@discardableResult
public func login(withParams params: LoginParams,
callback: @escaping Request<AuthenticationToken>.Callback)
-> Request<AuthenticationToken>? {
let request: Request<AuthenticationToken>? = self.request(toEndpoint: APIAdminEndpoint.login(params: params)) { result in
switch result {
case let .success(data: authenticationToken):
self.config.credentials.update(withAuthenticationToken: authenticationToken)
callback(.success(data: authenticationToken))
case let .fail(error):
callback(.fail(error: error))
}
}
return request
}
}
25 changes: 25 additions & 0 deletions Tests/Admin/APITests/APIAdminndpointTest.swift
@@ -0,0 +1,25 @@
//
// APIAdminEndpointTest.swift
// Tests
//
// Created by Mederic Petit on 14/9/2018.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//

@testable import OmiseGO
import XCTest

class APIAdminEndpointTest: XCTestCase {
let validLoginParams = LoginParams(email: "email@example.com", password: "password")

func testPath() {
XCTAssertEqual(APIAdminEndpoint.login(params: self.validLoginParams).path, "/admin.login")
}

func testTask() {
switch APIClientEndpoint.login(params: self.validLoginParams).task {
case .requestParameters: break
default: XCTFail("Wrong task")
}
}
}
45 changes: 45 additions & 0 deletions Tests/Admin/APITests/AdminCredentialTests.swift
@@ -0,0 +1,45 @@
//
// AdminCredentialTests.swift
// Tests
//
// Created by Mederic Petit on 14/9/18.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//

@testable import OmiseGO
import XCTest

class AdminCredentialTests: XCTestCase {
func testIsAuthenticatedIsFalseWithoutAUserId() {
var credentials = AdminCredential()
credentials.authenticationToken = "123"
XCTAssertFalse(credentials.isAuthenticated())
}

func testIsAuthenticatedIsFalseWithoutAToken() {
var credentials = AdminCredential()
credentials.userId = "123"
XCTAssertFalse(credentials.isAuthenticated())
}

func testIsAuthenticatedIsTrueWithAUserIdAndAToken() {
let credentials = AdminCredential(userId: "123", authenticationToken: "123")
XCTAssertTrue(credentials.isAuthenticated())
}

func testUpdateCredentialSuccessfully() {
var credentials = AdminCredential()
XCTAssertNil(credentials.userId)
XCTAssertNil(credentials.authenticationToken)
let authenticationToken = StubGenerator.authenticationToken(token: "token", user: StubGenerator.user(id: "user_id"))
credentials.update(withAuthenticationToken: authenticationToken)
XCTAssertEqual(credentials.authenticationToken, "token")
XCTAssertEqual(credentials.userId, "user_id")
}

func testAuthenticationReturnsNilAfterInvalidating() {
var credentials = AdminCredential(userId: "123", authenticationToken: "123")
credentials.invalidate()
XCTAssertNil(try credentials.authentication())
}
}
36 changes: 36 additions & 0 deletions Tests/Admin/FixtureTests/FixtureAdminAPI.swift
@@ -0,0 +1,36 @@
//
// FixtureAdminAPI.swift
// Tests
//
// Created by Mederic Petit on 14/9/18.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//

@testable import OmiseGO

class FixtureAdminAPI: HTTPAdminAPI {
let fixturesDirectoryURL: URL

init(fixturesDirectoryURL: URL, config: AdminConfiguration) {
self.fixturesDirectoryURL = fixturesDirectoryURL
super.init(config: config)
}

@discardableResult
override func request<ResultType>(toEndpoint endpoint: APIEndpoint,
callback: Request<ResultType>.Callback?) -> Request<ResultType>? {
do {
let request: FixtureRequest<ResultType> = FixtureRequest(fixturesDirectoryURL: self.fixturesDirectoryURL,
client: self,
endpoint: endpoint,
callback: callback)
return try request.start()
} catch let error as NSError {
operationQueue.addOperation { callback?(.fail(error: .other(error: error))) }
} catch let error as OMGError {
operationQueue.addOperation { callback?(.fail(error: error)) }
}

return nil
}
}
20 changes: 20 additions & 0 deletions Tests/Admin/FixtureTests/FixtureAdminTestCase.swift
@@ -0,0 +1,20 @@
//
// FixtureAdminTestCase.swift
// Tests
//
// Created by Mederic Petit on 14/9/18.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//

import OmiseGO
import XCTest

class FixtureAdminTestCase: XCTestCase {
var testClient: FixtureAdminAPI {
let bundle = Bundle(for: FixtureAdminTestCase.self)
let url = bundle.url(forResource: "admin_fixtures", withExtension: nil)!
let credentials = AdminCredential(userId: "user_id", authenticationToken: "token")
let config = AdminConfiguration(baseURL: "http://localhost:4000", credentials: credentials)
return FixtureAdminAPI(fixturesDirectoryURL: url, config: config)
}
}
39 changes: 39 additions & 0 deletions Tests/Admin/FixtureTests/LoginAdminFixtureTests.swift
@@ -0,0 +1,39 @@
//
// LoginAdminFixtureTests.swift
// Tests
//
// Created by Mederic Petit on 14/9/18.
// Copyright © 2017-2018 Omise Go Pte. Ltd. All rights reserved.
//

@testable import OmiseGO
import XCTest

class LoginAdminFixtureTests: XCTestCase {
var testClient: FixtureAdminAPI {
let bundle = Bundle(for: LoginAdminFixtureTests.self)
let url = bundle.url(forResource: "admin_fixtures", withExtension: nil)!
let config = AdminConfiguration(baseURL: "http://localhst:4000")
return FixtureAdminAPI(fixturesDirectoryURL: url, config: config)
}

func testLoginSuccessfullyAndUpdateToken() {
let expectation = self.expectation(description: "Log an admin in successfully and updates the client authentication")
XCTAssertNil(try! self.testClient.config.credentials.authentication())
let client = self.testClient
let params = LoginParams(email: "email", password: "password")
let request = client.login(withParams: params, callback: { result in
defer { expectation.fulfill() }
switch result {
case let .fail(error: error):
XCTFail(error.message)
case let .success(data: authenticationToken):
XCTAssertEqual(authenticationToken.token, "azJRj09l7jvR8KhTqUs3")
XCTAssertEqual(authenticationToken.user.id, "usr_01cc02x0v98qcctvycfx4vsk8x")
XCTAssertNotNil(try! client.config.credentials.authentication())
}
})
XCTAssertNotNil(request)
waitForExpectations(timeout: 15.0, handler: nil)
}
}