Skip to content

Commit 43c77ce

Browse files
fix: url percent encoding according to RFC 3986 (#781)
1 parent 2a211aa commit 43c77ce

File tree

4 files changed

+33
-33
lines changed

4 files changed

+33
-33
lines changed

Sources/AlgoliaSearchClient/Models/Search/Query/Query+URLEncodable.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ extension Query {
101101
}
102102

103103
func encode() -> String? {
104-
var components = URLComponents()
105-
components.queryItems = queryItems
106-
return components.query?.addingPercentEncoding(withAllowedCharacters: .uriAllowed)
104+
return queryItems
105+
.map { "\($0.name)=\($0.value?.addingPercentEncoding(withAllowedCharacters: .urlAllowed) ?? "")" }
106+
.joined(separator: "&")
107107
}
108108

109109
}

Sources/AlgoliaSearchClient/Transport/URLSession/URLRequest+Convenience.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension URLRequest: Builder {}
1414

1515
extension CharacterSet {
1616

17-
static var uriAllowed = CharacterSet.urlQueryAllowed.subtracting(.init(charactersIn: "+"))
17+
static let urlAllowed: CharacterSet = .alphanumerics.union(.init(charactersIn: "-._~")) // as per RFC 3986
1818

1919
}
2020

Tests/AlgoliaSearchClientTests/Unit/QueryTests.swift

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class QueryTests: XCTestCase {
1919
.set(\.explainModules, to: [.matchAlternatives])
2020
.set(\.attributesToRetrieve, to: ["attr1", "attr2", "attr3"])
2121
.set(\.restrictSearchableAttributes, to: ["rattr1", "rattr2"])
22-
.set(\.filters, to: "(color:red OR color:yellow) AND on-sale AND 12+")
22+
.set(\.filters, to: "(color:red OR color:yellow) AND on-sale AND 12+ AND (test:\"Hello & World\")")
2323
.set(\.facetFilters, to: [.or("color:red", "color:blue"), "size:M"])
2424
.set(\.optionalFilters, to: [.or("color:red", "color:yellow"), "on-sale"])
2525
.set(\.numericFilters, to: [.or("price>100", "length<1000"), "metrics>5"])
@@ -93,23 +93,23 @@ class QueryTests: XCTestCase {
9393
"distinct=5",
9494
"getRankingInfo=true",
9595
"explainModules=match.alternatives",
96-
"attributesToRetrieve=attr1,attr2,attr3",
97-
"restrictSearchableAttributes=rattr1,rattr2",
98-
"filters=(color:red%20OR%20color:yellow)%20AND%20on-sale%20AND%2012%2B",
99-
"facetFilters=%5B%5B%22color:red%22,%22color:blue%22%5D,%22size:M%22%5D",
100-
"optionalFilters=%5B%5B%22color:red%22,%22color:yellow%22%5D,%22on-sale%22%5D",
101-
"numericFilters=%5B%5B%22price%3E100%22,%22length%3C1000%22%5D,%22metrics%3E5%22%5D",
102-
"tagFilters=%5B%5B%22tag1%22,%22tag2%22%5D,%22tag3%22%5D",
96+
"attributesToRetrieve=attr1%2Cattr2%2Cattr3",
97+
"restrictSearchableAttributes=rattr1%2Crattr2",
98+
"filters=%28color%3Ared%20OR%20color%3Ayellow%29%20AND%20on-sale%20AND%2012%2B%20AND%20%28test%3A%22Hello%20%26%20World%22%29",
99+
"facetFilters=%5B%5B%22color%3Ared%22%2C%22color%3Ablue%22%5D%2C%22size%3AM%22%5D",
100+
"optionalFilters=%5B%5B%22color%3Ared%22%2C%22color%3Ayellow%22%5D%2C%22on-sale%22%5D",
101+
"numericFilters=%5B%5B%22price%3E100%22%2C%22length%3C1000%22%5D%2C%22metrics%3E5%22%5D",
102+
"tagFilters=%5B%5B%22tag1%22%2C%22tag2%22%5D%2C%22tag3%22%5D",
103103
"sumOrFiltersScores=false",
104-
"facets=facet1,facet2,facet3",
104+
"facets=facet1%2Cfacet2%2Cfacet3",
105105
"maxValuesPerFacet=10",
106106
"facetingAfterDistinct=true",
107107
"sortFacetValuesBy=count",
108108
"maxFacetHits=100",
109-
"attributesToHighlight=hattr1,hattr2,hattr3",
110-
"attributesToSnippet=sattr1:10,sattr2",
109+
"attributesToHighlight=hattr1%2Chattr2%2Chattr3",
110+
"attributesToSnippet=sattr1%3A10%2Csattr2",
111111
"highlightPreTag=%3Chl%3E",
112-
"highlightPostTag=%3C/hl%3E",
112+
"highlightPostTag=%3C%2Fhl%3E",
113113
"snippetEllipsisText=read%20more",
114114
"restrictHighlightAndSnippetArrays=true",
115115
"page=15",
@@ -120,41 +120,41 @@ class QueryTests: XCTestCase {
120120
"minWordSizefor2Typos=4",
121121
"typoTolerance=strict",
122122
"allowTyposOnNumericTokens=false",
123-
"disableTypoToleranceOnAttributes=dtattr1,dtattr2",
124-
"aroundLatLng=79.5,10.5",
123+
"disableTypoToleranceOnAttributes=dtattr1%2Cdtattr2",
124+
"aroundLatLng=79.5%2C10.5",
125125
"aroundLatLngViaIP=true",
126126
"aroundRadius=80",
127-
"aroundPrecision=%5B%7B%22from%22:0,%22value%22:1000%7D,%7B%22from%22:0,%22value%22:100000%7D%5D",
127+
"aroundPrecision=%5B%7B%22from%22%3A0%2C%22value%22%3A1000%7D%2C%7B%22from%22%3A0%2C%22value%22%3A100000%7D%5D",
128128
"minimumAroundRadius=40",
129-
"insideBoundingBox=%5B%5B0.0,10.0,20.0,30.0%5D,%5B40.0,50.0,60.0,70.0%5D%5D",
130-
"insidePolygon=%5B%5B0.0,10.0,20.0,30.0,40.0,50.0%5D,%5B10.0,20.0,30.0,40.0,50.0,60.0%5D%5D",
129+
"insideBoundingBox=%5B%5B0.0%2C10.0%2C20.0%2C30.0%5D%2C%5B40.0%2C50.0%2C60.0%2C70.0%5D%5D",
130+
"insidePolygon=%5B%5B0.0%2C10.0%2C20.0%2C30.0%2C40.0%2C50.0%5D%2C%5B10.0%2C20.0%2C30.0%2C40.0%2C50.0%2C60.0%5D%5D",
131131
"queryType=prefixLast",
132132
"removeWordsIfNoResults=lastWords",
133133
"advancedSyntax=false",
134-
"advancedSyntaxFeatures=exactPhrase,excludeWords",
135-
"optionalWords=optWord1,optWord2",
136-
"removeStopWords=ar,fr",
137-
"disableExactOnAttributes=deAttr1,deAttr2",
134+
"advancedSyntaxFeatures=exactPhrase%2CexcludeWords",
135+
"optionalWords=optWord1%2CoptWord2",
136+
"removeStopWords=ar%2Cfr",
137+
"disableExactOnAttributes=deAttr1%2CdeAttr2",
138138
"exactOnSingleWordQuery=word",
139-
"alternativesAsExact=ignorePlurals,singleWordSynonym",
139+
"alternativesAsExact=ignorePlurals%2CsingleWordSynonym",
140140
"ignorePlurals=false",
141-
"queryLanguages=hi,sq",
141+
"queryLanguages=hi%2Csq",
142142
"decompoundQuery=false",
143143
"enableRules=true",
144-
"ruleContexts=rc1,rc2",
144+
"ruleContexts=rc1%2Crc2",
145145
"enablePersonalization=false",
146146
"personalizationImpact=5",
147147
"userToken=testUserToken",
148148
"analytics=true",
149-
"analyticsTags=at1,at2,at3",
149+
"analyticsTags=at1%2Cat2%2Cat3",
150150
"enableABTest=false",
151151
"clickAnalytics=true",
152152
"synonyms=false",
153153
"replaceSynonymsInHighlight=true",
154154
"minProximity=3",
155-
"responseFields=facets_stats,hits",
155+
"responseFields=facets_stats%2Chits",
156156
"percentileComputation=false",
157-
"naturalLanguages=mi,ta",
157+
"naturalLanguages=mi%2Cta",
158158
"enableReRanking=true",
159159
"custom1=val1",
160160
"custom2=2.0",
@@ -189,7 +189,7 @@ class QueryTests: XCTestCase {
189189
"explainModules": ["match.alternatives"],
190190
"attributesToRetrieve": ["attr1", "attr2", "attr3"],
191191
"restrictSearchableAttributes": ["rattr1", "rattr2"],
192-
"filters": "(color:red OR color:yellow) AND on-sale AND 12+",
192+
"filters": "(color:red OR color:yellow) AND on-sale AND 12+ AND (test:\"Hello & World\")",
193193
"facetFilters": [["color:red", "color:blue"], "size:M"],
194194
"optionalFilters": [["color:red", "color:yellow"], "on-sale"],
195195
"numericFilters": [["price>100", "length<1000"], "metrics>5"],

Tests/AlgoliaSearchClientTests/Unit/SecuredAPIKeyRestrictionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class SecuredAPIKeyRestrictionTests: XCTestCase {
2222
validUntil: validUntil,
2323
userToken: "testUserToken")
2424

25-
XCTAssertEqual(restriction.urlEncodedString, "query=testQuery&clickAnalytics=true&restrictIndices=index1,index2&restrictSources=127.0.0.1,127.0.0.2&userToken=testUserToken&validUntil=\(Int(validUntil))")
25+
XCTAssertEqual(restriction.urlEncodedString, "query=testQuery&clickAnalytics=true&restrictIndices=index1%2Cindex2&restrictSources=127.0.0.1%2C127.0.0.2&userToken=testUserToken&validUntil=\(Int(validUntil))")
2626
}
2727

2828
}

0 commit comments

Comments
 (0)