Support runtime customisation of the rageshake URL. (#4267)
This commit is contained in:
@@ -372,7 +372,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
private static func setupServiceLocator(appSettings: AppSettings, appHooks: AppHooks) {
|
private static func setupServiceLocator(appSettings: AppSettings, appHooks: AppHooks) {
|
||||||
ServiceLocator.shared.register(userIndicatorController: UserIndicatorController())
|
ServiceLocator.shared.register(userIndicatorController: UserIndicatorController())
|
||||||
ServiceLocator.shared.register(appSettings: appSettings)
|
ServiceLocator.shared.register(appSettings: appSettings)
|
||||||
ServiceLocator.shared.register(bugReportService: BugReportService(baseURL: appSettings.bugReportServiceBaseURL,
|
ServiceLocator.shared.register(bugReportService: BugReportService(rageshakeURL: appSettings.bugReportRageshakeURL,
|
||||||
applicationID: appSettings.bugReportApplicationID,
|
applicationID: appSettings.bugReportApplicationID,
|
||||||
sdkGitSHA: sdkGitSha(),
|
sdkGitSHA: sdkGitSha(),
|
||||||
maxUploadSize: appSettings.bugReportMaxUploadSize,
|
maxUploadSize: appSettings.bugReportMaxUploadSize,
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ final class AppSettings {
|
|||||||
|
|
||||||
// MARK: - Bug report
|
// MARK: - Bug report
|
||||||
|
|
||||||
let bugReportServiceBaseURL: URL? = Secrets.rageshakeServerURL.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
let bugReportRageshakeURL: URL? = Secrets.rageshakeURL.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
||||||
let bugReportSentryURL: URL? = Secrets.sentryDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
let bugReportSentryURL: URL? = Secrets.sentryDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
||||||
let bugReportSentryRustURL: URL? = Secrets.sentryRustDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
let bugReportSentryRustURL: URL? = Secrets.sentryRustDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping
|
||||||
/// The name allocated by the bug report server
|
/// The name allocated by the bug report server
|
||||||
|
|||||||
@@ -2142,6 +2142,47 @@ class BugReportServiceMock: BugReportServiceProtocol, @unchecked Sendable {
|
|||||||
var underlyingCrashedLastRun: Bool!
|
var underlyingCrashedLastRun: Bool!
|
||||||
var lastCrashEventID: String?
|
var lastCrashEventID: String?
|
||||||
|
|
||||||
|
//MARK: - applyConfiguration
|
||||||
|
|
||||||
|
var applyConfigurationUnderlyingCallsCount = 0
|
||||||
|
var applyConfigurationCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return applyConfigurationUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = applyConfigurationUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
applyConfigurationUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
applyConfigurationUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var applyConfigurationCalled: Bool {
|
||||||
|
return applyConfigurationCallsCount > 0
|
||||||
|
}
|
||||||
|
var applyConfigurationReceivedConfiguration: RageshakeConfiguration?
|
||||||
|
var applyConfigurationReceivedInvocations: [RageshakeConfiguration] = []
|
||||||
|
var applyConfigurationClosure: ((RageshakeConfiguration) -> Void)?
|
||||||
|
|
||||||
|
func applyConfiguration(_ configuration: RageshakeConfiguration) {
|
||||||
|
applyConfigurationCallsCount += 1
|
||||||
|
applyConfigurationReceivedConfiguration = configuration
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.applyConfigurationReceivedInvocations.append(configuration)
|
||||||
|
}
|
||||||
|
applyConfigurationClosure?(configuration)
|
||||||
|
}
|
||||||
//MARK: - submitBugReport
|
//MARK: - submitBugReport
|
||||||
|
|
||||||
var submitBugReportProgressListenerUnderlyingCallsCount = 0
|
var submitBugReportProgressListenerUnderlyingCallsCount = 0
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
|||||||
switch await widgetDriver.start(baseURL: baseURL,
|
switch await widgetDriver.start(baseURL: baseURL,
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
colorScheme: colorScheme,
|
colorScheme: colorScheme,
|
||||||
rageshakeURL: appSettings.bugReportServiceBaseURL?.absoluteString,
|
rageshakeURL: appSettings.bugReportRageshakeURL?.absoluteString,
|
||||||
analyticsConfiguration: analyticsConfiguration) {
|
analyticsConfiguration: analyticsConfiguration) {
|
||||||
case .success(let url):
|
case .success(let url):
|
||||||
state.url = url
|
state.url = url
|
||||||
|
|||||||
@@ -12,27 +12,30 @@ import Sentry
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class BugReportService: NSObject, BugReportServiceProtocol {
|
class BugReportService: NSObject, BugReportServiceProtocol {
|
||||||
private let baseURL: URL?
|
/// The rageshake URL as provided in the init.
|
||||||
|
private let defaultRageshakeURL: URL?
|
||||||
|
/// The rageshake URL currently being used by the service.
|
||||||
|
private var rageshakeURL: URL?
|
||||||
private let applicationID: String
|
private let applicationID: String
|
||||||
private let sdkGitSHA: String
|
private let sdkGitSHA: String
|
||||||
private let maxUploadSize: Int
|
private let maxUploadSize: Int
|
||||||
private let session: URLSession
|
private let session: URLSession
|
||||||
|
|
||||||
private let appHooks: AppHooks
|
private let appHooks: AppHooks
|
||||||
|
|
||||||
private let progressSubject = PassthroughSubject<Double, Never>()
|
private let progressSubject = PassthroughSubject<Double, Never>()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
var isEnabled: Bool { baseURL != nil }
|
var isEnabled: Bool { rageshakeURL != nil }
|
||||||
var lastCrashEventID: String?
|
var lastCrashEventID: String?
|
||||||
|
|
||||||
init(baseURL: URL?,
|
init(rageshakeURL: URL?,
|
||||||
applicationID: String,
|
applicationID: String,
|
||||||
sdkGitSHA: String,
|
sdkGitSHA: String,
|
||||||
maxUploadSize: Int,
|
maxUploadSize: Int,
|
||||||
session: URLSession = .shared,
|
session: URLSession = .shared,
|
||||||
appHooks: AppHooks) {
|
appHooks: AppHooks) {
|
||||||
self.baseURL = baseURL
|
defaultRageshakeURL = rageshakeURL
|
||||||
|
self.rageshakeURL = rageshakeURL
|
||||||
self.applicationID = applicationID
|
self.applicationID = applicationID
|
||||||
self.sdkGitSHA = sdkGitSHA
|
self.sdkGitSHA = sdkGitSHA
|
||||||
self.maxUploadSize = maxUploadSize
|
self.maxUploadSize = maxUploadSize
|
||||||
@@ -47,10 +50,21 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
|||||||
SentrySDK.crashedLastRun
|
SentrySDK.crashedLastRun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyConfiguration(_ configuration: RageshakeConfiguration) {
|
||||||
|
switch configuration {
|
||||||
|
case .url(let url):
|
||||||
|
rageshakeURL = url
|
||||||
|
case .disabled:
|
||||||
|
rageshakeURL = nil
|
||||||
|
case .default:
|
||||||
|
rageshakeURL = defaultRageshakeURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// swiftlint:disable:next cyclomatic_complexity
|
// swiftlint:disable:next cyclomatic_complexity
|
||||||
func submitBugReport(_ bugReport: BugReport,
|
func submitBugReport(_ bugReport: BugReport,
|
||||||
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
|
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
|
||||||
guard let baseURL else {
|
guard let rageshakeURL else {
|
||||||
fatalError("No bug report URL set, the screen should not be shown in this case.")
|
fatalError("No bug report URL set, the screen should not be shown in this case.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +121,7 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
|||||||
}
|
}
|
||||||
body.appendString(string: "--\(boundary)--\r\n")
|
body.appendString(string: "--\(boundary)--\r\n")
|
||||||
|
|
||||||
var request = URLRequest(url: baseURL)
|
var request = URLRequest(url: rageshakeURL)
|
||||||
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
|
|||||||
@@ -46,6 +46,15 @@ enum BugReportServiceError: LocalizedError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RageshakeConfiguration {
|
||||||
|
/// Rageshakes should be sent to the provided URL
|
||||||
|
case url(URL)
|
||||||
|
/// Rageshakes are disabled.
|
||||||
|
case disabled
|
||||||
|
/// No customisations are made, use the default configuration.
|
||||||
|
case `default`
|
||||||
|
}
|
||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
protocol BugReportServiceProtocol: AnyObject {
|
protocol BugReportServiceProtocol: AnyObject {
|
||||||
var isEnabled: Bool { get }
|
var isEnabled: Bool { get }
|
||||||
@@ -53,6 +62,8 @@ protocol BugReportServiceProtocol: AnyObject {
|
|||||||
|
|
||||||
var lastCrashEventID: String? { get set }
|
var lastCrashEventID: String? { get set }
|
||||||
|
|
||||||
|
func applyConfiguration(_ configuration: RageshakeConfiguration)
|
||||||
|
|
||||||
func submitBugReport(_ bugReport: BugReport,
|
func submitBugReport(_ bugReport: BugReport,
|
||||||
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>
|
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule Enterprise updated: a3ede7e4de...0c0b8ae6bc
@@ -13,7 +13,7 @@ sentryDSN: String? = read?("env:SENTRY_DSN")
|
|||||||
sentryRustDSN: String? = read?("env:SENTRY_RUST_DSN")
|
sentryRustDSN: String? = read?("env:SENTRY_RUST_DSN")
|
||||||
postHogHost: String? = read?("env:POSTHOG_HOST")
|
postHogHost: String? = read?("env:POSTHOG_HOST")
|
||||||
postHogAPIKey: String? = read?("env:POSTHOG_API_KEY")
|
postHogAPIKey: String? = read?("env:POSTHOG_API_KEY")
|
||||||
rageshakeServerURL: String? = read?("env:RAGESHAKE_SERVER_URL")
|
rageshakeURL: String? = read?("env:RAGESHAKE_URL")
|
||||||
mapLibreAPIKey: String? = read?("env:MAPLIBRE_API_KEY")
|
mapLibreAPIKey: String? = read?("env:MAPLIBRE_API_KEY")
|
||||||
|
|
||||||
output {
|
output {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum Secrets {
|
|||||||
static let sentryRustDSN: String? = "https://username@sentry.localhost/project_id"
|
static let sentryRustDSN: String? = "https://username@sentry.localhost/project_id"
|
||||||
static let postHogHost: String? = "https://posthog.localhost"
|
static let postHogHost: String? = "https://posthog.localhost"
|
||||||
static let postHogAPIKey: String? = "your_key"
|
static let postHogAPIKey: String? = "your_key"
|
||||||
static let rageshakeServerURL: String? = "https://rageshake.localhost"
|
static let rageshakeURL: String? = "https://rageshake.localhost/submit"
|
||||||
static let mapLibreAPIKey: String? = "your_key"
|
static let mapLibreAPIKey: String? = "your_key"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ class BugReportServiceTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testInitialStateWithRealService() throws {
|
func testInitialStateWithRealService() throws {
|
||||||
let service = BugReportService(baseURL: "https://www.example.com",
|
let service = BugReportService(rageshakeURL: "https://example.com/submit",
|
||||||
applicationID: "mock_app_id",
|
applicationID: "mock_app_id",
|
||||||
sdkGitSHA: "1234",
|
sdkGitSHA: "1234",
|
||||||
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
||||||
@@ -53,7 +53,7 @@ class BugReportServiceTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testInitialStateWithRealServiceAndNoURL() throws {
|
func testInitialStateWithRealServiceAndNoURL() throws {
|
||||||
let service = BugReportService(baseURL: nil,
|
let service = BugReportService(rageshakeURL: nil,
|
||||||
applicationID: "mock_app_id",
|
applicationID: "mock_app_id",
|
||||||
sdkGitSHA: "1234",
|
sdkGitSHA: "1234",
|
||||||
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
||||||
@@ -64,7 +64,7 @@ class BugReportServiceTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor func testSubmitBugReportWithRealService() async throws {
|
@MainActor func testSubmitBugReportWithRealService() async throws {
|
||||||
let service = BugReportService(baseURL: "https://www.example.com",
|
let service = BugReportService(rageshakeURL: "https://example.com/submit",
|
||||||
applicationID: "mock_app_id",
|
applicationID: "mock_app_id",
|
||||||
sdkGitSHA: "1234",
|
sdkGitSHA: "1234",
|
||||||
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
||||||
@@ -86,6 +86,62 @@ class BugReportServiceTests: XCTestCase {
|
|||||||
XCTAssertEqual(response.reportURL, "https://example.com/123")
|
XCTAssertEqual(response.reportURL, "https://example.com/123")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor func testConfigurations() async throws {
|
||||||
|
let service = BugReportService(rageshakeURL: "https://example.com/submit",
|
||||||
|
applicationID: "mock_app_id",
|
||||||
|
sdkGitSHA: "1234",
|
||||||
|
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
||||||
|
session: .mock,
|
||||||
|
appHooks: AppHooks())
|
||||||
|
XCTAssertTrue(service.isEnabled)
|
||||||
|
|
||||||
|
service.applyConfiguration(.disabled)
|
||||||
|
XCTAssertFalse(service.isEnabled)
|
||||||
|
|
||||||
|
service.applyConfiguration(.url("https://bugs.server.net/submit"))
|
||||||
|
XCTAssertTrue(service.isEnabled)
|
||||||
|
|
||||||
|
let bugReport = BugReport(userID: "@mock:client.com",
|
||||||
|
deviceID: nil,
|
||||||
|
ed25519: nil,
|
||||||
|
curve25519: nil,
|
||||||
|
text: "i cannot send message",
|
||||||
|
logFiles: Tracing.logFiles,
|
||||||
|
canContact: false,
|
||||||
|
githubLabels: [],
|
||||||
|
files: [])
|
||||||
|
let progressSubject = CurrentValueSubject<Double, Never>(0.0)
|
||||||
|
let customConfigurationResponse = try await service.submitBugReport(bugReport, progressListener: progressSubject).get()
|
||||||
|
|
||||||
|
XCTAssertEqual(customConfigurationResponse.reportURL, "https://bugs.server.net/123")
|
||||||
|
|
||||||
|
service.applyConfiguration(.default)
|
||||||
|
XCTAssertTrue(service.isEnabled)
|
||||||
|
|
||||||
|
let defaultConfigurationResponse = try await service.submitBugReport(bugReport, progressListener: progressSubject).get()
|
||||||
|
|
||||||
|
XCTAssertEqual(defaultConfigurationResponse.reportURL, "https://example.com/123")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisabledConfigurations() {
|
||||||
|
let service = BugReportService(rageshakeURL: nil,
|
||||||
|
applicationID: "mock_app_id",
|
||||||
|
sdkGitSHA: "1234",
|
||||||
|
maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize,
|
||||||
|
session: .mock,
|
||||||
|
appHooks: AppHooks())
|
||||||
|
XCTAssertFalse(service.isEnabled)
|
||||||
|
|
||||||
|
service.applyConfiguration(.disabled)
|
||||||
|
XCTAssertFalse(service.isEnabled)
|
||||||
|
|
||||||
|
service.applyConfiguration(.url("https://bugs.server.net/submit"))
|
||||||
|
XCTAssertTrue(service.isEnabled)
|
||||||
|
|
||||||
|
service.applyConfiguration(.default)
|
||||||
|
XCTAssertFalse(service.isEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
func testLogsMaxSize() {
|
func testLogsMaxSize() {
|
||||||
// Given a new set of logs
|
// Given a new set of logs
|
||||||
var logs = BugReportService.Logs(maxFileSize: 1000)
|
var logs = BugReportService.Logs(maxFileSize: 1000)
|
||||||
@@ -114,9 +170,11 @@ class BugReportServiceTests: XCTestCase {
|
|||||||
|
|
||||||
private class MockURLProtocol: URLProtocol {
|
private class MockURLProtocol: URLProtocol {
|
||||||
override func startLoading() {
|
override func startLoading() {
|
||||||
let response = "{\"report_url\":\"https://example.com/123\"}"
|
guard let url = request.url else { return }
|
||||||
|
let reportURL = url.deletingLastPathComponent().appending(path: "123")
|
||||||
|
let response = "{\"report_url\":\"\(reportURL.absoluteString)\"}"
|
||||||
|
|
||||||
if let data = response.data(using: .utf8),
|
if let data = response.data(using: .utf8),
|
||||||
let url = request.url,
|
|
||||||
let urlResponse = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil) {
|
let urlResponse = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil) {
|
||||||
client?.urlProtocol(self, didReceive: urlResponse, cacheStoragePolicy: .allowedInMemoryOnly)
|
client?.urlProtocol(self, didReceive: urlResponse, cacheStoragePolicy: .allowedInMemoryOnly)
|
||||||
client?.urlProtocol(self, didLoad: data)
|
client?.urlProtocol(self, didLoad: data)
|
||||||
|
|||||||
Reference in New Issue
Block a user