Skip to content

Commit

Permalink
Created InternalAPI for "health" request (#1971)
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoSoto authored Oct 20, 2022
1 parent 461b007 commit f06a8ce
Show file tree
Hide file tree
Showing 27 changed files with 420 additions and 15 deletions.
14 changes: 11 additions & 3 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
572247D127BEC28E00C524A7 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572247D027BEC28E00C524A7 /* Array+Extensions.swift */; };
572247F727BF1ADF00C524A7 /* ArrayExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572247F627BF1ADF00C524A7 /* ArrayExtensionsTests.swift */; };
5722482727C2BD3200C524A7 /* LockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5722482627C2BD3200C524A7 /* LockTests.swift */; };
5723FBBE28EDE1360003BA16 /* InternalAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5723FBBD28EDE1360003BA16 /* InternalAPI.swift */; };
5733B18E27FF586A00EC2045 /* BackendError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5733B18D27FF586A00EC2045 /* BackendError.swift */; };
5733B1A427FF9F8300EC2045 /* NetworkErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5733B1A327FF9F8300EC2045 /* NetworkErrorTests.swift */; };
5733B1A827FFBCC800EC2045 /* BackendErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5733B1A727FFBCC800EC2045 /* BackendErrorTests.swift */; };
Expand Down Expand Up @@ -292,6 +293,7 @@
5796A3A927D7C43500653165 /* Deprecations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A3A827D7C43500653165 /* Deprecations.swift */; };
5796A3C027D7D64500653165 /* ResultExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A3BF27D7D64500653165 /* ResultExtensionsTests.swift */; };
579B67F428C5326A0094F7E8 /* PaymentQueueWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579B67F328C5326A0094F7E8 /* PaymentQueueWrapper.swift */; };
579D2E3A28F0BF5A0094B36F /* BackendInternalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579D2E3928F0BF5A0094B36F /* BackendInternalTests.swift */; };
57A0FBF02749C0C2009E2FC3 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A0FBEF2749C0C2009E2FC3 /* Atomic.swift */; };
57A0FBF22749CF66009E2FC3 /* SynchronizedUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A0FBF12749CF66009E2FC3 /* SynchronizedUserDefaults.swift */; };
57A17727276A721D0052D3A8 /* Set+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A17726276A721D0052D3A8 /* Set+Extensions.swift */; };
Expand Down Expand Up @@ -734,6 +736,7 @@
572247D027BEC28E00C524A7 /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
572247F627BF1ADF00C524A7 /* ArrayExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtensionsTests.swift; sourceTree = "<group>"; };
5722482627C2BD3200C524A7 /* LockTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockTests.swift; sourceTree = "<group>"; };
5723FBBD28EDE1360003BA16 /* InternalAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalAPI.swift; sourceTree = "<group>"; };
5733B18D27FF586A00EC2045 /* BackendError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendError.swift; sourceTree = "<group>"; };
5733B1A327FF9F8300EC2045 /* NetworkErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkErrorTests.swift; sourceTree = "<group>"; };
5733B1A727FFBCC800EC2045 /* BackendErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendErrorTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -807,6 +810,7 @@
5796A3A827D7C43500653165 /* Deprecations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecations.swift; sourceTree = "<group>"; };
5796A3BF27D7D64500653165 /* ResultExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultExtensionsTests.swift; sourceTree = "<group>"; };
579B67F328C5326A0094F7E8 /* PaymentQueueWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentQueueWrapper.swift; sourceTree = "<group>"; };
579D2E3928F0BF5A0094B36F /* BackendInternalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendInternalTests.swift; sourceTree = "<group>"; };
57A0FBEF2749C0C2009E2FC3 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
57A0FBF12749CF66009E2FC3 /* SynchronizedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedUserDefaults.swift; sourceTree = "<group>"; };
57A17726276A721D0052D3A8 /* Set+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Set+Extensions.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1614,6 +1618,7 @@
B34605D0279A6E600031CA74 /* CustomerAPI.swift */,
B3781567285A79FC000A7B93 /* IdentityAPI.swift */,
B378156B285A9729000A7B93 /* OfferingsAPI.swift */,
5723FBBD28EDE1360003BA16 /* InternalAPI.swift */,
);
path = Networking;
sourceTree = "<group>";
Expand Down Expand Up @@ -1785,16 +1790,17 @@
isa = PBXGroup;
children = (
5796A39727D6C07D00653165 /* __Snapshots__ */,
A56C2E002819C33500995421 /* BackendPostAdServicesTokenTests.swift */,
5796A38927D6B96300653165 /* BackendGetCustomerInfoTests.swift */,
5796A38F27D6BCD100653165 /* BackendGetIntroEligibilityTests.swift */,
5796A39327D6BD6900653165 /* BackendGetOfferingsTests.swift */,
579D2E3928F0BF5A0094B36F /* BackendInternalTests.swift */,
5796A38B27D6BA1600653165 /* BackendLoginTests.swift */,
A56C2E002819C33500995421 /* BackendPostAdServicesTokenTests.swift */,
A56DFDF1286665E600EF2E32 /* BackendPostAttributionDataTests.swift */,
5796A39527D6BDAB00653165 /* BackendPostOfferForSigningTests.swift */,
5796A39827D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift */,
5796A38727D6B85900653165 /* BackendPostReceiptDataTests.swift */,
5796A39827D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift */,
5796A38027D6B78500653165 /* BaseBackendTest.swift */,
A56DFDF1286665E600EF2E32 /* BackendPostAttributionDataTests.swift */,
);
path = Backend;
sourceTree = "<group>";
Expand Down Expand Up @@ -2389,6 +2395,7 @@
57DE806D28074976008D6C6F /* Storefront.swift in Sources */,
B3B5FBB6269CED6400104A0C /* ErrorDetails.swift in Sources */,
2D991ACA268BA56900085481 /* StoreKitRequestFetcher.swift in Sources */,
5723FBBE28EDE1360003BA16 /* InternalAPI.swift in Sources */,
A55D5D66282ECCC100FA7623 /* PostAdServicesTokenOperation.swift in Sources */,
B3B5FBB4269CED4B00104A0C /* BackendErrorCode.swift in Sources */,
2DDF41B624F6F387005BC22D /* ASN1ObjectIdentifierBuilder.swift in Sources */,
Expand Down Expand Up @@ -2686,6 +2693,7 @@
57ACB13728184CF1000DCC9F /* DecoderExtensionTests.swift in Sources */,
351B516126D44BEB00BD2BD7 /* IdentityManagerTests.swift in Sources */,
57032ABF28C13CE4004FF47A /* StoreKit2SettingTests.swift in Sources */,
579D2E3A28F0BF5A0094B36F /* BackendInternalTests.swift in Sources */,
351B51C126D450E800BD2BD7 /* OfferingsManagerTests.swift in Sources */,
5796A39927D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift in Sources */,
35D8330A262FBA9A00E60AC5 /* MockUserDefaults.swift in Sources */,
Expand Down
13 changes: 13 additions & 0 deletions Sources/FoundationExtensions/Result+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ extension Result {

}

extension Result where Success == Void {

/// Creates a `Result<Void, Error>` with an optional `Error`.
init(_ error: Failure?) {
if let error = error {
self = .failure(error)
} else {
self = .success(())
}
}

}

extension Result where Success: OptionalType {

/// Converts a `Result<Success?, Error>` into `Result<Success, Error>?`
Expand Down
16 changes: 13 additions & 3 deletions Sources/Networking/Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ class Backend {

let identity: IdentityAPI
let offerings: OfferingsAPI
let customer: CustomerAPI
let internalAPI: InternalAPI

private let config: BackendConfiguration
private let customer: CustomerAPI

convenience init(apiKey: String,
systemInfo: SystemInfo,
Expand All @@ -43,17 +44,26 @@ class Backend {
let customer = CustomerAPI(backendConfig: backendConfig, attributionFetcher: attributionFetcher)
let identity = IdentityAPI(backendConfig: backendConfig)
let offerings = OfferingsAPI(backendConfig: backendConfig)
self.init(backendConfig: backendConfig, customerAPI: customer, identityAPI: identity, offeringsAPI: offerings)
let internalAPI = InternalAPI(backendConfig: backendConfig)

self.init(backendConfig: backendConfig,
customerAPI: customer,
identityAPI: identity,
offeringsAPI: offerings,
internalAPI: internalAPI)
}

required init(backendConfig: BackendConfiguration,
customerAPI: CustomerAPI,
identityAPI: IdentityAPI,
offeringsAPI: OfferingsAPI) {
offeringsAPI: OfferingsAPI,
internalAPI: InternalAPI) {
self.config = backendConfig

self.customer = customerAPI
self.identity = identityAPI
self.offerings = offeringsAPI
self.internalAPI = internalAPI
}

func clearHTTPClientCaches() {
Expand Down
10 changes: 5 additions & 5 deletions Sources/Networking/HTTPClient/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ class HTTPClient {
self.dnsChecker = dnsChecker
self.timeout = requestTimeout
self.apiKey = apiKey
self.authHeaders = HTTPClient.authorizationHeader(withAPIKey: apiKey)
self.authHeaders = HTTPClient.authorizationHeader(withAPIKey: apiKey)
}

func perform<Value: HTTPResponseBody>(_ request: HTTPRequest, completionHandler: Completion<Value>?) {
perform(request: .init(httpRequest: request,
headers: self.authHeaders,
completionHandler: completionHandler))
self.perform(request: .init(httpRequest: request,
headers: request.path.authenticated ? self.authHeaders : [:],
completionHandler: completionHandler))
}

func clearCaches() {
eTagManager.clearCaches()
self.eTagManager.clearCaches()
}

}
Expand Down
27 changes: 27 additions & 0 deletions Sources/Networking/HTTPClient/HTTPRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,35 @@ extension HTTPRequest {
case postReceiptData
case postSubscriberAttributes(appUserID: String)
case postAdServicesToken(appUserID: String)
case health

}

}

extension HTTPRequest.Path {

/// Whether requests to this path are authenticated.
var authenticated: Bool {
switch self {
case .getCustomerInfo,
.getOfferings,
.getIntroEligibility,
.logIn,
.postAttributionData,
.postOfferForSigning,
.postReceiptData,
.postSubscriberAttributes,
.postAdServicesToken:
return true

case .health:
return false
}
}

}

extension HTTPRequest.Path: Hashable {}

extension HTTPRequest.Path: CustomStringConvertible {
Expand Down Expand Up @@ -106,6 +130,9 @@ extension HTTPRequest.Path: CustomStringConvertible {

case let .postSubscriberAttributes(appUserID):
return "subscribers/\(appUserID)/attributes"

case .health:
return "health"
}
}

Expand Down
85 changes: 85 additions & 0 deletions Sources/Networking/InternalAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// InternalAPI.swift
//
// Created by Nacho Soto on 10/5/22.

import Foundation

final class InternalAPI {

typealias ResponseHandler = (BackendError?) -> Void

private let backendConfig: BackendConfiguration
private let callbackCache: CallbackCache<HealthOperation.Callback>

init(backendConfig: BackendConfiguration) {
self.backendConfig = backendConfig
self.callbackCache = .init()
}

func healthRequest(completion: @escaping ResponseHandler) {
let operation = HealthOperation(httpClient: self.backendConfig.httpClient,
callbackCache: self.callbackCache)

let callback = HealthOperation.Callback(cacheKey: operation.cacheKey, completion: completion)
let cacheStatus = self.callbackCache.add(callback)

self.backendConfig.addCacheableOperation(operation,
withRandomDelay: false,
cacheStatus: cacheStatus)
}

}

// MARK: - Health

private class HealthOperation: CacheableNetworkOperation {

struct Callback: CacheKeyProviding {

let cacheKey: String
let completion: InternalAPI.ResponseHandler

}

struct Configuration: NetworkConfiguration {

let httpClient: HTTPClient

}

private let callbackCache: CallbackCache<Callback>

init(httpClient: HTTPClient,
callbackCache: CallbackCache<Callback>) {
self.callbackCache = callbackCache

super.init(configuration: Configuration(httpClient: httpClient),
individualizedCacheKeyPart: "")
}

override func begin(completion: @escaping () -> Void) {
self.httpClient.perform(
.init(method: .get, path: .health)
) { (response: HTTPResponse<HTTPEmptyResponseBody>.Result) in
self.callbackCache.performOnAllItemsAndRemoveFromCache(withCacheable: self) { callback in
callback.completion(
response
.mapError(BackendError.networkError)
.error
)
}

completion()
}
}

}
10 changes: 10 additions & 0 deletions Tests/UnitTests/FoundationExtensions/ResultExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ class ResultExtensionsTests: TestCase {
}
}

func testVoidValueInitWithNoError() {
expect(Result<Void, Error>(nil)).to(beSuccess())
}

func testVoidValueInitWithError() {
expect(Result<Void, Error>(.error1)).to(beFailure {
expect($0).to(matchError(Error.error1))
})
}

}

class ResultAsOptionalResultTest: TestCase {
Expand Down
8 changes: 7 additions & 1 deletion Tests/UnitTests/Mocks/MockBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ class MockBackend: Backend {
let identity = MockIdentityAPI(backendConfig: backendConfig)
let offerings = MockOfferingsAPI(backendConfig: backendConfig)
let customer = CustomerAPI(backendConfig: backendConfig, attributionFetcher: attributionFetcher)
self.init(backendConfig: backendConfig, customerAPI: customer, identityAPI: identity, offeringsAPI: offerings)
let internalAPI = InternalAPI(backendConfig: backendConfig)

self.init(backendConfig: backendConfig,
customerAPI: customer,
identityAPI: identity,
offeringsAPI: offerings,
internalAPI: internalAPI)
}

override func post(receiptData: Data,
Expand Down
3 changes: 2 additions & 1 deletion Tests/UnitTests/Mocks/MockHTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class MockHTTPClient: HTTPClient {
private let sourceTestFile: StaticString

override func perform<Value: HTTPResponseBody>(_ request: HTTPRequest, completionHandler: Completion<Value>?) {
let call = Call(request: request, headers: authHeaders)
let call = Call(request: request,
headers: request.path.authenticated ? self.authHeaders : [:])

DispatchQueue.main.async {
self.calls.append(call)
Expand Down
Loading

0 comments on commit f06a8ce

Please sign in to comment.