Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import uniffi.wp_api.WpApiClientDelegate
import uniffi.wp_api.WpApiMiddlewarePipeline
import uniffi.wp_api.WpAuthenticationProvider
import uniffi.wp_api.WpErrorCode
import uniffi.wp_api.ParsedUrl
import uniffi.wp_mobile.MockPostService
import uniffi.wp_mobile.SiteInfo
import uniffi.wp_mobile.WpService
import rs.wordpress.cache.kotlin.WordPressApiCache

Expand Down Expand Up @@ -51,10 +53,15 @@ fun createTestServiceContext(): TestServiceContext {
// Extract site URL by removing /wp-json suffix
val siteUrl = apiRootUrl.removeSuffix("/wp-json")

val parsedSiteUrl = ParsedUrl.parse(siteUrl)
val parsedApiRoot = ParsedUrl.parse(apiRootUrl)

// Create self-hosted service
val service = WpService.selfHosted(
siteUrl = siteUrl,
apiRoot = apiRootUrl,
val service = WpService(
siteInfo = SiteInfo.SelfHosted(
siteUrl = parsedSiteUrl,
apiRoot = parsedApiRoot
),
delegate = WpApiClientDelegate(
authProvider,
requestExecutor = WpRequestExecutor(emptyList(), NetworkAvailabilityProvider { true }),
Expand All @@ -64,11 +71,12 @@ fun createTestServiceContext(): TestServiceContext {
cache = wordPressApiCache.cache
)

// Create mock post service with shared cache
// Create mock post service with shared cache.
// Use the parsed URL strings to ensure URL normalization matches the WpService's DbSite.
val mockPostService = MockPostService(
wordPressApiCache.cache,
siteUrl,
apiRootUrl
parsedSiteUrl.url(),
parsedApiRoot.url()
)

return TestServiceContext(service, mockPostService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import uniffi.wp_api.parseAuthorizationUrl
import uniffi.wp_api.wordpressComOauth2Configuration
import uniffi.wp_mobile.Account
import uniffi.wp_mobile.AccountRepository
import uniffi.wp_mobile.wordpressComSiteApiRoot

class WelcomeActivity : ComponentActivity() {
private val accountRepository: AccountRepository by inject()
Expand Down Expand Up @@ -212,16 +211,14 @@ class WelcomeActivity : ComponentActivity() {
val tokenResponse = tokenResult.response.data
val blogId = tokenResponse.blogId
?: throw IllegalStateException("Expected blog_id in site-specific token response")
val siteUrl = discoveredSiteHost
?: tokenResponse.blogUrl
?: "WordPress.com"
accountRepository.store(
Account.SelfHostedSite(
Account.WpCom(
id = 0uL,
domain = siteUrl,
username = siteUrl,
password = tokenResponse.accessToken,
siteApiRoot = wordpressComSiteApiRoot(blogId)
username = discoveredSiteHost
?: tokenResponse.blogUrl
?: "WordPress.com",
token = tokenResponse.accessToken,
siteApiRoot = blogId.toString()
)
)
siteSpecificOAuthState = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,17 @@ fun App(authenticationEnabled: Boolean, authenticateSite: (String, onSuccess: ()
navController.navigate("site")
}
is Account.WpCom -> {
currentWpComClient = createWpComApiClient(account)
navController.navigate("wpcom_site")
if (account.siteApiRoot.isNotEmpty()) {
// Site-specific WP.com account with a blog ID
currentWpService = createWpService(account, cache)
val apiClient = createWpApiClient(account)
currentApiClient = apiClient
currentSiteViewModel = SiteViewModel(apiClient)
navController.navigate("site")
} else {
currentWpComClient = createWpComApiClient(account)
navController.navigate("wpcom_site")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import uniffi.wp_api.WpAuthenticationProvider
import uniffi.wp_api.wpAuthenticationFromUsernameAndPassword
import uniffi.wp_mobile.Account
import uniffi.wp_mobile.AccountRepository
import uniffi.wp_api.ParsedUrl
import uniffi.wp_api.WpComBaseUrl
import uniffi.wp_api.WpComDotOrgApiUrlResolver
import uniffi.wp_mobile.SiteInfo
import uniffi.wp_mobile.WpService
import java.io.File
import java.net.URI
Expand Down Expand Up @@ -47,9 +51,11 @@ fun createWpService(
} else {
wpAuthenticationFromUsernameAndPassword(account.username, account.password)
}
return WpService.selfHosted(
siteUrl = account.domain,
apiRoot = account.siteApiRoot,
return WpService(
siteInfo = SiteInfo.SelfHosted(
siteUrl = ParsedUrl.parse(account.domain),
apiRoot = ParsedUrl.parse(account.siteApiRoot)
),
delegate = WpApiClientDelegate(
WpAuthenticationProvider.staticWithAuth(auth),
requestExecutor = WpRequestExecutor(emptyList(), networkAvailabilityProvider),
Expand All @@ -60,6 +66,28 @@ fun createWpService(
)
}

fun createWpService(
account: Account.WpCom,
cache: WordPressApiCache,
networkAvailabilityProvider: NetworkAvailabilityProvider = NetworkAvailabilityProvider { true }
): WpService {
val siteId = account.siteApiRoot.toULong()
return WpService(
siteInfo = SiteInfo.WordPressCom(
siteId = siteId
),
delegate = WpApiClientDelegate(
WpAuthenticationProvider.staticWithAuth(
WpAuthentication.Bearer(token = account.token)
),
requestExecutor = WpRequestExecutor(emptyList(), networkAvailabilityProvider),
middlewarePipeline = WpApiMiddlewarePipeline(listOf(DebugMiddleware())),
appNotifier = EmptyAppNotifier()
),
cache = cache.cache
)
}

fun createWpApiClient(
account: Account.SelfHostedSite,
networkAvailabilityProvider: NetworkAvailabilityProvider = NetworkAvailabilityProvider { true }
Expand All @@ -77,6 +105,22 @@ fun createWpApiClient(
)
}

fun createWpApiClient(
account: Account.WpCom,
networkAvailabilityProvider: NetworkAvailabilityProvider = NetworkAvailabilityProvider { true }
): WpApiClient {
return WpApiClient(
apiUrlResolver = WpComDotOrgApiUrlResolver(
siteId = account.siteApiRoot,
baseUrl = WpComBaseUrl.Production
),
authProvider = WpAuthenticationProvider.staticWithAuth(
WpAuthentication.Bearer(token = account.token)
),
requestExecutor = WpRequestExecutor(emptyList(), networkAvailabilityProvider)
)
}

fun createWpComApiClient(
account: Account.WpCom,
networkAvailabilityProvider: NetworkAvailabilityProvider = NetworkAvailabilityProvider { true }
Expand Down
6 changes: 5 additions & 1 deletion native/swift/Example/Example/WordPressAPI+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ extension WordPressAPI {
throw CocoaError(.xpcConnectionInvalid)
}

let siteUrl = try ParsedUrl.parse(input: apiRootUrl.asURL().deletingLastPathComponent().absoluteString)
return WordPressAPI(
urlSession: .shared,
apiRootUrl: apiRootUrl,
siteInfo: .selfHosted(
siteUrl: siteUrl,
apiRoot: apiRootUrl
),
authentication: loginCredentials,
middlewarePipeline: MiddlewarePipeline(middlewares: [
DebugMiddleware()
Expand Down
1 change: 1 addition & 0 deletions native/swift/Sources/wordpress-api/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public typealias UserRole = WordPressAPIInternal.UserRole

// MARK: - Service Layer
public typealias WpService = WordPressAPIInternal.WpService
public typealias SiteInfo = WordPressAPIInternal.SiteInfo
public typealias AnyPostFilter = WordPressAPIInternal.AnyPostFilter
public typealias WpApiCache = WordPressAPIInternal.WpApiCache

Expand Down
47 changes: 21 additions & 26 deletions native/swift/Sources/wordpress-api/WordPressAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public final class WordPressAPI: Sendable {
case unableToParseResponse
}

private let apiUrlResolver: ApiUrlResolver
private let siteInfo: SiteInfo
private let urlSession: URLSession

let requestExecutor: SafeRequestExecutor
Expand All @@ -26,13 +26,13 @@ public final class WordPressAPI: Sendable {
public convenience init(
urlSession: URLSession,
notifyingDelegate: URLSessionTaskDelegate? = nil,
apiRootUrl: ParsedUrl,
siteInfo: SiteInfo,
authentication: WpAuthentication,
middlewarePipeline: MiddlewarePipeline = .default,
appNotifier: WpAppNotifier? = nil
) {
self.init(
apiUrlResolver: WpOrgSiteApiUrlResolver(apiRootUrl: apiRootUrl),
siteInfo: siteInfo,
authenticationProvider: .staticWithAuth(auth: authentication),
executor: WpRequestExecutor(urlSession: urlSession, notifyingDelegate: notifyingDelegate),
middlewarePipeline: middlewarePipeline,
Expand All @@ -42,13 +42,13 @@ public final class WordPressAPI: Sendable {

public convenience init(
urlSession: URLSession,
apiRootUrl: ParsedUrl,
siteInfo: SiteInfo,
authenticationProvider: WpAuthenticationProvider,
middlewarePipeline: MiddlewarePipeline = .default,
appNotifier: WpAppNotifier? = nil
) {
self.init(
apiUrlResolver: WpOrgSiteApiUrlResolver(apiRootUrl: apiRootUrl),
siteInfo: siteInfo,
authenticationProvider: authenticationProvider,
executor: WpRequestExecutor(urlSession: urlSession),
middlewarePipeline: middlewarePipeline,
Expand All @@ -59,13 +59,13 @@ public final class WordPressAPI: Sendable {
public convenience init(
urlSession: URLSession,
notifyingDelegate: URLSessionTaskDelegate? = nil,
apiUrlResolver: ApiUrlResolver,
siteInfo: SiteInfo,
authenticationProvider: WpAuthenticationProvider,
middlewarePipeline: MiddlewarePipeline = .default,
appNotifier: WpAppNotifier? = nil
) {
self.init(
apiUrlResolver: apiUrlResolver,
siteInfo: siteInfo,
authenticationProvider: authenticationProvider,
executor: WpRequestExecutor(urlSession: urlSession, notifyingDelegate: notifyingDelegate),
middlewarePipeline: middlewarePipeline,
Expand All @@ -76,22 +76,23 @@ public final class WordPressAPI: Sendable {
public convenience init(
urlSession: URLSession,
notifyingDelegate: URLSessionTaskDelegate? = nil,
siteUrl: String,
siteUrl: ParsedUrl,
apiRootUrl: ParsedUrl,
username: String,
password: String,
middlewarePipeline: MiddlewarePipeline = .default,
appNotifier: WpAppNotifier? = nil
) {
let siteInfo = SiteInfo.selfHosted(siteUrl: siteUrl, apiRoot: apiRootUrl)
let executor = WpRequestExecutor(urlSession: urlSession, notifyingDelegate: notifyingDelegate)
let provider = CookiesNonceAuthenticationProvider.withSiteUrl(
url: siteUrl,
url: siteUrl.url(),
username: username,
password: password,
requestExecutor: executor
)
self.init(
apiUrlResolver: WpOrgSiteApiUrlResolver(apiRootUrl: apiRootUrl),
siteInfo: siteInfo,
authenticationProvider: .dynamic(dynamicAuthenticationProvider: provider),
executor: executor,
middlewarePipeline: middlewarePipeline,
Expand All @@ -108,6 +109,10 @@ public final class WordPressAPI: Sendable {
middlewarePipeline: MiddlewarePipeline = .default,
appNotifier: WpAppNotifier? = nil
) {
let siteInfo = SiteInfo.selfHosted(
siteUrl: details.parsedSiteUrl,
apiRoot: details.apiRootUrl
)
let executor = WpRequestExecutor(urlSession: urlSession, notifyingDelegate: notifyingDelegate)
let provider = CookiesNonceAuthenticationProvider(
username: username,
Expand All @@ -116,7 +121,7 @@ public final class WordPressAPI: Sendable {
requestExecutor: executor
)
self.init(
apiUrlResolver: WpOrgSiteApiUrlResolver(apiRootUrl: details.apiRootUrl),
siteInfo: siteInfo,
authenticationProvider: .dynamic(dynamicAuthenticationProvider: provider),
executor: executor,
middlewarePipeline: middlewarePipeline,
Expand All @@ -126,39 +131,29 @@ public final class WordPressAPI: Sendable {

init(
urlSession: URLSession = .shared,
apiUrlResolver: ApiUrlResolver,
siteInfo: SiteInfo,
authenticationProvider: WpAuthenticationProvider,
executor: SafeRequestExecutor,
middlewarePipeline: MiddlewarePipeline,
appNotifier: WpAppNotifier?
) {
self.urlSession = urlSession
self.apiUrlResolver = apiUrlResolver
self.siteInfo = siteInfo
self.apiClientDelegate = WpApiClientDelegate(
authProvider: authenticationProvider,
requestExecutor: executor,
middlewarePipeline: middlewarePipeline,
appNotifier: appNotifier ?? EmptyAppNotifier()
)
self.requestBuilder = UniffiWpApiClient(
apiUrlResolver: self.apiUrlResolver,
apiUrlResolver: siteInfo.apiUrlResolver(),
delegate: self.apiClientDelegate
)
self.requestExecutor = executor
}

public func createSelfHostedService(cache: WordPressApiCache) throws -> WpService {
let apiURL = apiUrlResolver.resolve(namespace: "", endpointSegments: []).asURL()
return try WpService.selfHosted(
siteUrl: apiURL.deletingLastPathComponent().absoluteString,
apiRoot: apiURL.absoluteString,
delegate: apiClientDelegate,
cache: cache.cache
)
}

public func createWordPressComService(siteId: WpComSiteId, cache: WordPressApiCache) throws -> WpService {
try WpService.wordpressCom(siteId: siteId, delegate: apiClientDelegate, cache: cache.cache)
public func createService(cache: WordPressApiCache) throws -> WpService {
try WpService(siteInfo: siteInfo, delegate: apiClientDelegate, cache: cache.cache)
}

public var users: UsersRequestExecutor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ public final class WordPressLoginClient: @unchecked Sendable {
let nonceRetrieval = WpRestNonceRetrieval(details: details, requestExecutor: requestExecutor)
let nonce = try await nonceRetrieval.getNonce(username: username, password: password)
return WordPressAPI(
apiUrlResolver: WpOrgSiteApiUrlResolver(apiRootUrl: details.apiRootUrl),
siteInfo: .selfHosted(
siteUrl: details.parsedSiteUrl,
apiRoot: details.apiRootUrl
),
authenticationProvider: .staticWithAuth(auth: .nonce(nonce: nonce)),
executor: requestExecutor,
middlewarePipeline: middleware,
Expand Down
10 changes: 9 additions & 1 deletion native/swift/Tests/integration-tests/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ func restoreTestServer() async throws {
}

extension TestCredentials {
var siteURL: ParsedUrl {
// swiftlint:disable:next force_try
try! ParsedUrl.parse(input: siteUrl)
}

var apiRootURL: ParsedUrl {
// swiftlint:disable:next force_try
try! ParsedUrl.parse(input: siteUrl + "/wp-json")
Expand All @@ -27,7 +32,10 @@ extension WordPressAPI {
return WordPressAPI(
urlSession: .init(configuration: .ephemeral),
notifyingDelegate: notifyingDelegate,
apiRootUrl: credentials.apiRootURL,
siteInfo: .selfHosted(
siteUrl: credentials.siteURL,
apiRoot: credentials.apiRootURL
),
authentication: credentials.adminAuthentication
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ struct NonceAuthenticationTests {
let credentials = TestCredentials.instance()
let api = WordPressAPI(
urlSession: .init(configuration: .ephemeral),
siteUrl: credentials.siteUrl,
siteUrl: credentials.siteURL,
apiRootUrl: credentials.apiRootURL,
username: credentials.adminUsername,
password: credentials.adminAccountPassword
Expand Down
Loading