Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 2c2b4c3

Browse files
authored
Merge pull request #576 from wordpress-mobile/feature/jetpack-proxy-remote
Add Jetpack Proxy remote service
2 parents 1aa3671 + 4e60447 commit 2c2b4c3

File tree

4 files changed

+195
-7
lines changed

4 files changed

+195
-7
lines changed

WordPressKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Pod::Spec.new do |s|
44
s.name = 'WordPressKit'
5-
s.version = '6.0.0'
5+
s.version = '6.1.0-beta.1'
66

77
s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.'
88
s.description = <<-DESC

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,14 @@
124124
46ABD0EA262EEE0400C7FF24 /* AppTransportSecuritySettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46ABD0E9262EEE0400C7FF24 /* AppTransportSecuritySettingsTests.swift */; };
125125
4A1DEF44293051BC00322608 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A1DEF43293051BC00322608 /* LoggingTests.swift */; };
126126
4A1DEF46293051C600322608 /* LoggingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1DEF45293051C600322608 /* LoggingTests.m */; };
127-
4A68E3DD294070A7004AC3DC /* RemoteReaderSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3DC294070A7004AC3DC /* RemoteReaderSite.swift */; };
128-
4A68E3DF29407100004AC3DC /* RemoteReaderTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3DE29407100004AC3DC /* RemoteReaderTopic.swift */; };
129-
4A68E3E1294076C1004AC3DC /* RemoteReaderSiteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3E0294076C1004AC3DC /* RemoteReaderSiteInfo.swift */; };
130127
4A68E3CD29404181004AC3DC /* RemoteBlog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3CC29404181004AC3DC /* RemoteBlog.swift */; };
131128
4A68E3CF29404289004AC3DC /* RemoteBlogOptionsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3CE29404289004AC3DC /* RemoteBlogOptionsHelper.swift */; };
132129
4A68E3D329406AA0004AC3DC /* RemoteMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D029406AA0004AC3DC /* RemoteMenu.swift */; };
133130
4A68E3D429406AA0004AC3DC /* RemoteMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */; };
134131
4A68E3D529406AA0004AC3DC /* RemoteMenuLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */; };
132+
4A68E3DD294070A7004AC3DC /* RemoteReaderSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3DC294070A7004AC3DC /* RemoteReaderSite.swift */; };
133+
4A68E3DF29407100004AC3DC /* RemoteReaderTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3DE29407100004AC3DC /* RemoteReaderTopic.swift */; };
134+
4A68E3E1294076C1004AC3DC /* RemoteReaderSiteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3E0294076C1004AC3DC /* RemoteReaderSiteInfo.swift */; };
135135
57BCD3D426209D9500292CB3 /* AppTransportSecuritySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */; };
136136
730E869F21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 730E869E21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift */; };
137137
731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */; };
@@ -613,7 +613,9 @@
613613
FE20A6A6282BC68D0025E975 /* blogging-prompts-settings-fetch-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FE20A6A5282BC68D0025E975 /* blogging-prompts-settings-fetch-success.json */; };
614614
FE20A6A8282BC83A0025E975 /* blogging-prompts-settings-update-with-response.json in Resources */ = {isa = PBXBuildFile; fileRef = FE20A6A7282BC83A0025E975 /* blogging-prompts-settings-update-with-response.json */; };
615615
FE20A6AA282BC8710025E975 /* blogging-prompts-settings-update-empty-response.json in Resources */ = {isa = PBXBuildFile; fileRef = FE20A6A9282BC8710025E975 /* blogging-prompts-settings-update-empty-response.json */; };
616+
FEAE3AC7298AC2A300E05A24 /* JetpackProxyServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAE3AC6298AC2A300E05A24 /* JetpackProxyServiceRemote.swift */; };
616617
FEB7A88F271873BD00A8CF85 /* reader-post-comments-update-notification-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FEB7A88E271873BD00A8CF85 /* reader-post-comments-update-notification-success.json */; };
618+
FED77253298B819900C2346E /* JetpackProxyServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED77252298B819900C2346E /* JetpackProxyServiceRemoteTests.swift */; };
617619
FEE4EF57272FDD4B003CDA3C /* RemoteCommentV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */; };
618620
FEE4EF59272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF58272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift */; };
619621
FEE4EF5B27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF5A27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift */; };
@@ -778,14 +780,14 @@
778780
46ABD0E9262EEE0400C7FF24 /* AppTransportSecuritySettingsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppTransportSecuritySettingsTests.swift; sourceTree = "<group>"; };
779781
4A1DEF43293051BC00322608 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
780782
4A1DEF45293051C600322608 /* LoggingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoggingTests.m; sourceTree = "<group>"; };
781-
4A68E3DC294070A7004AC3DC /* RemoteReaderSite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderSite.swift; sourceTree = "<group>"; };
782-
4A68E3DE29407100004AC3DC /* RemoteReaderTopic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderTopic.swift; sourceTree = "<group>"; };
783-
4A68E3E0294076C1004AC3DC /* RemoteReaderSiteInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderSiteInfo.swift; sourceTree = "<group>"; };
784783
4A68E3CC29404181004AC3DC /* RemoteBlog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteBlog.swift; sourceTree = "<group>"; };
785784
4A68E3CE29404289004AC3DC /* RemoteBlogOptionsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteBlogOptionsHelper.swift; sourceTree = "<group>"; };
786785
4A68E3D029406AA0004AC3DC /* RemoteMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenu.swift; sourceTree = "<group>"; };
787786
4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuItem.swift; sourceTree = "<group>"; };
788787
4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuLocation.swift; sourceTree = "<group>"; };
788+
4A68E3DC294070A7004AC3DC /* RemoteReaderSite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderSite.swift; sourceTree = "<group>"; };
789+
4A68E3DE29407100004AC3DC /* RemoteReaderTopic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderTopic.swift; sourceTree = "<group>"; };
790+
4A68E3E0294076C1004AC3DC /* RemoteReaderSiteInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderSiteInfo.swift; sourceTree = "<group>"; };
789791
57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTransportSecuritySettings.swift; sourceTree = "<group>"; };
790792
6C2A33D76FD1052D6F30466D /* Pods-WordPressKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKit/Pods-WordPressKit.debug.xcconfig"; sourceTree = "<group>"; };
791793
6F2E0CC4FA01B5475A378DA2 /* Pods-WordPressKitTests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release-alpha.xcconfig"; sourceTree = "<group>"; };
@@ -1278,7 +1280,9 @@
12781280
FE20A6A5282BC68D0025E975 /* blogging-prompts-settings-fetch-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blogging-prompts-settings-fetch-success.json"; sourceTree = "<group>"; };
12791281
FE20A6A7282BC83A0025E975 /* blogging-prompts-settings-update-with-response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blogging-prompts-settings-update-with-response.json"; sourceTree = "<group>"; };
12801282
FE20A6A9282BC8710025E975 /* blogging-prompts-settings-update-empty-response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blogging-prompts-settings-update-empty-response.json"; sourceTree = "<group>"; };
1283+
FEAE3AC6298AC2A300E05A24 /* JetpackProxyServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackProxyServiceRemote.swift; sourceTree = "<group>"; };
12811284
FEB7A88E271873BD00A8CF85 /* reader-post-comments-update-notification-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "reader-post-comments-update-notification-success.json"; sourceTree = "<group>"; };
1285+
FED77252298B819900C2346E /* JetpackProxyServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackProxyServiceRemoteTests.swift; sourceTree = "<group>"; };
12821286
FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteCommentV2.swift; sourceTree = "<group>"; };
12831287
FEE4EF58272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentServiceRemoteREST+ApiV2.swift"; sourceTree = "<group>"; };
12841288
FEE4EF5A27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentServiceRemoteREST+APIv2Tests.swift"; sourceTree = "<group>"; };
@@ -1822,6 +1826,7 @@
18221826
8B749DEC25AF3E4600023F03 /* JetpackCapabilitiesServiceRemote.swift */,
18231827
32FC1D27255C91ED00CD0A7B /* JetpackScanServiceRemote.swift */,
18241828
9A2D0B27225E0119009E585F /* JetpackServiceRemote.swift */,
1829+
FEAE3AC6298AC2A300E05A24 /* JetpackProxyServiceRemote.swift */,
18251830
439A44D52107C66A00795ED7 /* JSONDecoderExtension.swift */,
18261831
74DA56361F06EB0500FE9BF4 /* MediaServiceRemote.h */,
18271832
74DA562E1F06EAF000FE9BF4 /* MediaServiceRemoteREST.h */,
@@ -2330,6 +2335,7 @@
23302335
3297E1DC2564649D00287D21 /* Scan */,
23312336
9A2D0B2A225E0E22009E585F /* JetpackServiceRemoteTests.swift */,
23322337
8B749E8125AF7DDA00023F03 /* JetpackCapabilitiesServiceRemoteTests.swift */,
2338+
FED77252298B819900C2346E /* JetpackProxyServiceRemoteTests.swift */,
23332339
);
23342340
name = Jetpack;
23352341
sourceTree = "<group>";
@@ -3027,6 +3033,7 @@
30273033
464BAB0B262F6736006AEED5 /* RemoteBlockEditorSettings.swift in Sources */,
30283034
FEFFD99126C1347D00F34231 /* ShareAppContentServiceRemote.swift in Sources */,
30293035
74585B8E1F0D51A100E7E667 /* DomainsServiceRemote.swift in Sources */,
3036+
FEAE3AC7298AC2A300E05A24 /* JetpackProxyServiceRemote.swift in Sources */,
30303037
4625B965253A343900C04AAD /* RemotePageLayouts.swift in Sources */,
30313038
FEF7419B2808591C002C4203 /* BloggingPromptsServiceRemote.swift in Sources */,
30323039
7430C9A41F1927180051B8E6 /* ReaderPostServiceRemote.m in Sources */,
@@ -3266,6 +3273,7 @@
32663273
FEE4EF5B27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift in Sources */,
32673274
803DE81128FFA9C4007D4E9C /* RemoteConfigRemoteTests.swift in Sources */,
32683275
74B5F0DE1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m in Sources */,
3276+
FED77253298B819900C2346E /* JetpackProxyServiceRemoteTests.swift in Sources */,
32693277
ABD95B7F25DD6C4B00735BEE /* CommentServiceRemoteRESTLikesTests.swift in Sources */,
32703278
8B749E8225AF7DDA00023F03 /* JetpackCapabilitiesServiceRemoteTests.swift in Sources */,
32713279
74E2294B1F1E73340085F7F2 /* SharingServiceRemoteTests.m in Sources */,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/// Encapsulates Jetpack Proxy requests.
2+
public class JetpackProxyServiceRemote: ServiceRemoteWordPressComREST {
3+
4+
/// Represents the most common HTTP methods for the proxied request.
5+
public enum DotComMethod: String {
6+
case get
7+
case post
8+
case put
9+
case delete
10+
}
11+
12+
/// Sends a proxied request to a Jetpack-connected site through the Jetpack Proxy API.
13+
/// The proxy API expects the client to be authenticated with a WordPress.com account.
14+
///
15+
/// - Parameters:
16+
/// - siteID: The dotcom ID of the Jetpack-connected site.
17+
/// - path: The request endpoint to be proxied.
18+
/// - method: The HTTP method for the proxied request.
19+
/// - parameters: The request parameter for the proxied request. Defaults to empty.
20+
/// - locale: The user locale, if any. Defaults to nil.
21+
/// - completion: Closure called after the request completes.
22+
/// - Returns: A Progress object, which can be used to cancel the request if needed.
23+
@discardableResult
24+
public func proxyRequest(for siteID: Int,
25+
path: String,
26+
method: DotComMethod,
27+
parameters: [String: AnyHashable] = [:],
28+
locale: String? = nil,
29+
completion: @escaping (Result<AnyObject, Error>) -> Void) -> Progress? {
30+
let urlString = self.path(forEndpoint: "jetpack-blogs/\(siteID)/rest-api", withVersion: ._1_1)
31+
32+
// Construct the request parameters to be forwarded to the actual endpoint.
33+
var requestParams: [String: AnyHashable] = [
34+
"json": "true",
35+
"path": "\(path)&_method=\(method.rawValue)"
36+
]
37+
38+
// The parameters need to be encoded into a JSON string.
39+
if !parameters.isEmpty,
40+
let data = try? JSONSerialization.data(withJSONObject: parameters, options: []),
41+
let jsonString = String(data: data, encoding: .utf8) {
42+
// Use "query" for the body parameters if the method is GET. Otherwise, always use "body".
43+
let bodyParameterKey = (method == .get ? "query" : "body")
44+
requestParams[bodyParameterKey] = jsonString
45+
}
46+
47+
if let locale,
48+
!locale.isEmpty {
49+
requestParams["locale"] = locale
50+
}
51+
52+
return wordPressComRestApi.POST(urlString, parameters: requestParams as [String: AnyObject]) { response, _ in
53+
completion(.success(response))
54+
} failure: { error, _ in
55+
completion(.failure(error))
56+
}
57+
}
58+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import XCTest
2+
@testable import WordPressKit
3+
4+
class JetpackProxyServiceRemoteTests: XCTestCase {
5+
let timeout: TimeInterval = 1.0
6+
let api = MockWordPressComRestApi()
7+
let siteID = 1001
8+
9+
private var remote: JetpackProxyServiceRemote {
10+
return .init(wordPressComRestApi: api)
11+
}
12+
13+
// MARK: - Tests
14+
15+
func testProxyRequestEndpoint() {
16+
// the mock rest API doesn't append the base URL, so we're just going to verify the path.
17+
let urlString = "rest/v1.1/jetpack-blogs/\(siteID)/rest-api"
18+
19+
remote.proxyRequest(for: siteID, path: "path", method: .get) { _ in }
20+
21+
guard let passedURLString = api.URLStringPassedIn else {
22+
return XCTFail()
23+
}
24+
XCTAssertTrue(passedURLString.hasSuffix(urlString))
25+
}
26+
27+
func testJSONParameter() {
28+
remote.proxyRequest(for: siteID, path: "path", method: .get) { _ in }
29+
30+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
31+
return XCTFail()
32+
}
33+
XCTAssertEqual(passedParameter["json"] as? String, "true")
34+
}
35+
36+
func testPathParameter() {
37+
let path = "/wp/v2/posts"
38+
let method = JetpackProxyServiceRemote.DotComMethod.get
39+
40+
remote.proxyRequest(for: siteID, path: path, method: method) { _ in }
41+
42+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
43+
return XCTFail()
44+
}
45+
XCTAssertEqual(passedParameter["path"] as? String, "\(path)&_method=\(method.rawValue)")
46+
}
47+
48+
func testBodyParameterKeyForGETMethod() {
49+
let method = JetpackProxyServiceRemote.DotComMethod.get
50+
let params = ["key": "value"]
51+
52+
remote.proxyRequest(for: siteID, path: "path", method: method, parameters: params) { _ in }
53+
54+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
55+
return XCTFail()
56+
}
57+
XCTAssertNotNil(passedParameter["query"])
58+
}
59+
60+
func testBodyParameterKeyForPOSTMethod() {
61+
let method = JetpackProxyServiceRemote.DotComMethod.post
62+
let params = ["key": "value"]
63+
64+
remote.proxyRequest(for: siteID, path: "path", method: method, parameters: params) { _ in }
65+
66+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
67+
return XCTFail()
68+
}
69+
XCTAssertNotNil(passedParameter["body"])
70+
}
71+
72+
func testBodyParameterEncoding() {
73+
let method = JetpackProxyServiceRemote.DotComMethod.post
74+
let params = [
75+
"key1": "value1",
76+
"key2": "value2"
77+
]
78+
79+
remote.proxyRequest(for: siteID, path: "path", method: method, parameters: params) { _ in }
80+
81+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject],
82+
let jsonString = passedParameter["body"] as? String,
83+
let jsonData = jsonString.data(using: .utf8),
84+
let jsonDictionary = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: String] else {
85+
return XCTFail()
86+
}
87+
XCTAssertEqual(jsonDictionary, params)
88+
}
89+
90+
func testBodyParameterShouldNotExistWhenEmpty() {
91+
let params = [String: String]()
92+
93+
remote.proxyRequest(for: siteID, path: "path", method: .post, parameters: params) { _ in }
94+
95+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
96+
return XCTFail()
97+
}
98+
XCTAssertNil(passedParameter["body"])
99+
}
100+
101+
func testLocaleParameter() {
102+
let locale = "en_US"
103+
104+
remote.proxyRequest(for: siteID, path: "path", method: .post, locale: locale) { _ in }
105+
106+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
107+
return XCTFail()
108+
}
109+
XCTAssertEqual(passedParameter["locale"] as? String, locale)
110+
}
111+
112+
func testLocaleParameterShouldNotExistWhenEmpty() {
113+
let locale = String()
114+
115+
remote.proxyRequest(for: siteID, path: "path", method: .post, locale: locale) { _ in }
116+
117+
guard let passedParameter = api.parametersPassedIn as? [String: AnyObject] else {
118+
return XCTFail()
119+
}
120+
XCTAssertNil(passedParameter["locale"])
121+
}
122+
}

0 commit comments

Comments
 (0)