Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(postgrest): increase code coverage #651

Merged
merged 2 commits into from
Jan 31, 2025
Merged
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
25 changes: 23 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/PostgREST.xcscheme
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand All @@ -26,8 +26,29 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PostgREST"
BuildableName = "PostgREST"
BlueprintName = "PostgREST"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PostgRESTTests"
BuildableName = "PostgRESTTests"
BlueprintName = "PostgRESTTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
60 changes: 58 additions & 2 deletions Sources/PostgREST/Deprecated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ extension PostgrestClient.Configuration {
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
message:
"Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
)
public init(
url: URL,
Expand Down Expand Up @@ -57,7 +58,8 @@ extension PostgrestClient {
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
message:
"Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
)
public convenience init(
url: URL,
Expand All @@ -78,3 +80,57 @@ extension PostgrestClient {
)
}
}

extension PostgrestFilterBuilder {

@available(*, deprecated, renamed: "like(_:pattern:)")
public func like(
_ column: String,
value: any URLQueryRepresentable
) -> PostgrestFilterBuilder {
like(column, pattern: value)
}

@available(*, deprecated, renamed: "in(_:values:)")
public func `in`(
_ column: String,
value: [any URLQueryRepresentable]
) -> PostgrestFilterBuilder {
`in`(column, values: value)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .plain type.")
public func plfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .plain)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .phrase type.")
public func phfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .phrase)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .websearch type.")
public func wfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .websearch)
}

@available(*, deprecated, renamed: "ilike(_:pattern:)")
public func ilike(
_ column: String,
value: any URLQueryRepresentable
) -> PostgrestFilterBuilder {
ilike(column, pattern: value)
}
}
6 changes: 1 addition & 5 deletions Sources/PostgREST/PostgrestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ public class PostgrestBuilder: @unchecked Sendable {
}

if let count = $0.fetchOptions.count {
if let prefer = $0.request.headers[.prefer] {
$0.request.headers[.prefer] = "\(prefer),count=\(count.rawValue)"
} else {
$0.request.headers[.prefer] = "count=\(count.rawValue)"
}
$0.request.headers.appendOrUpdate(.prefer, value: "count=\(count.rawValue)")
}

if $0.request.headers[.accept] == nil {
Expand Down
80 changes: 16 additions & 64 deletions Sources/PostgREST/PostgrestFilterBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
public class PostgrestFilterBuilder: PostgrestTransformBuilder, @unchecked Sendable {
public enum Operator: String, CaseIterable, Sendable {
case eq, neq, gt, gte, lt, lte, like, ilike, `is`, `in`, cs, cd, sl, sr, nxl, nxr, adj, ov, fts,
plfts, phfts, wfts
plfts, phfts, wfts
}

// MARK: - Filters
Expand All @@ -17,10 +17,11 @@
let queryValue = value.queryValue

mutableState.withValue {
$0.request.query.append(URLQueryItem(
name: column,
value: "not.\(op.rawValue).\(queryValue)"
))
$0.request.query.append(
URLQueryItem(
name: column,
value: "not.\(op.rawValue).\(queryValue)"
))
}

return self
Expand Down Expand Up @@ -152,14 +153,6 @@
return self
}

@available(*, deprecated, renamed: "like(_:pattern:)")
public func like(
_ column: String,
value: any URLQueryRepresentable
) -> PostgrestFilterBuilder {
like(column, pattern: value)
}

/// Match only rows where `column` matches all of `patterns` case-sensitively.
/// - Parameters:
/// - column: The column to filter on
Expand Down Expand Up @@ -206,14 +199,6 @@
return self
}

@available(*, deprecated, renamed: "ilike(_:pattern:)")
public func ilike(
_ column: String,
value: any URLQueryRepresentable
) -> PostgrestFilterBuilder {
ilike(column, pattern: value)
}

/// Match only rows where `column` matches all of `patterns` case-insensitively.
/// - Parameters:
/// - column: The column to filter on
Expand Down Expand Up @@ -284,14 +269,6 @@
return self
}

@available(*, deprecated, renamed: "in(_:values:)")
public func `in`(
_ column: String,
value: [any URLQueryRepresentable]
) -> PostgrestFilterBuilder {
`in`(column, values: value)
}

/// Match only rows where `column` contains every element appearing in `value`.
///
/// Only relevant for jsonb, array, and range columns.
Expand Down Expand Up @@ -472,33 +449,6 @@
textSearch(column, query: query, config: config, type: nil)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .plain type.")
public func plfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .plain)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .phrase type.")
public func phfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .phrase)
}

@available(*, deprecated, message: "Use textSearch(_:query:config:type) with .websearch type.")
public func wfts(
_ column: String,
query: any URLQueryRepresentable,
config: String? = nil
) -> PostgrestFilterBuilder {
textSearch(column, query: query, config: config, type: .websearch)
}

/// Match only rows which satisfy the filter. This is an escape hatch - you should use the specific filter methods wherever possible.
///
/// Unlike most filters, `opearator` and `value` are used as-is and need to follow [PostgREST syntax](https://postgrest.org/en/stable/api.html#operators). You also need to make sure they are properly sanitized.
Expand All @@ -513,10 +463,11 @@
value: String
) -> PostgrestFilterBuilder {
mutableState.withValue {
$0.request.query.append(URLQueryItem(
name: column,
value: "\(`operator`).\(value)"
))
$0.request.query.append(
URLQueryItem(
name: column,
value: "\(`operator`).\(value)"
))
}
return self
}
Expand All @@ -530,10 +481,11 @@
let query = query.mapValues(\.queryValue)
mutableState.withValue { mutableState in
for (key, value) in query {
mutableState.request.query.append(URLQueryItem(
name: key,
value: "eq.\(value.queryValue)"
))
mutableState.request.query.append(
URLQueryItem(
name: key,
value: "eq.\(value.queryValue)"
))
}
}
return self
Expand Down Expand Up @@ -624,7 +576,7 @@
query: String,
config: String? = nil
) -> PostgrestFilterBuilder {
plfts(column, query: query, config: config)

Check warning on line 579 in Sources/PostgREST/PostgrestFilterBuilder.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (15) (IOS, 15.2)

'plfts(_:query:config:)' is deprecated: Use textSearch(_:query:config:type) with .plain type.
}

public func phraseToFullTextSearch(
Expand All @@ -632,7 +584,7 @@
query: String,
config: String? = nil
) -> PostgrestFilterBuilder {
phfts(column, query: query, config: config)

Check warning on line 587 in Sources/PostgREST/PostgrestFilterBuilder.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (15) (IOS, 15.2)

'phfts(_:query:config:)' is deprecated: Use textSearch(_:query:config:type) with .phrase type.
}

public func webFullTextSearch(
Expand All @@ -640,6 +592,6 @@
query: String,
config: String? = nil
) -> PostgrestFilterBuilder {
wfts(column, query: query, config: config)

Check warning on line 595 in Sources/PostgREST/PostgrestFilterBuilder.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (15) (IOS, 15.2)

'wfts(_:query:config:)' is deprecated: Use textSearch(_:query:config:type) with .websearch type.
}
}
44 changes: 7 additions & 37 deletions Sources/PostgREST/PostgrestTransformBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
.joined(separator: "")
mutableState.withValue {
$0.request.query.appendOrUpdate(URLQueryItem(name: "select", value: cleanedColumns))

if let prefer = $0.request.headers[.prefer] {
var components = prefer.components(separatedBy: ",")

if let index = components.firstIndex(where: { $0.hasPrefix("return=") }) {
components[index] = "return=representation"
} else {
components.append("return=representation")
}

$0.request.headers[.prefer] = components.joined(separator: ",")
} else {
$0.request.headers[.prefer] = "return=representation"
}
$0.request.headers.appendOrUpdate(.prefer, value: "return=representation")
}
return self
}
Expand Down Expand Up @@ -64,7 +51,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
"\(column).\(ascending ? "asc" : "desc").\(nullsFirst ? "nullsfirst" : "nullslast")"

if let existingOrderIndex,
let currentValue = $0.request.query[existingOrderIndex].value
let currentValue = $0.request.query[existingOrderIndex].value
{
$0.request.query[existingOrderIndex] = URLQueryItem(
name: key,
Expand All @@ -85,11 +72,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
public func limit(_ count: Int, referencedTable: String? = nil) -> PostgrestTransformBuilder {
mutableState.withValue {
let key = referencedTable.map { "\($0).limit" } ?? "limit"
if let index = $0.request.query.firstIndex(where: { $0.name == key }) {
$0.request.query[index] = URLQueryItem(name: key, value: "\(count)")
} else {
$0.request.query.append(URLQueryItem(name: key, value: "\(count)"))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: key, value: "\(count)"))
}
return self
}
Expand All @@ -113,24 +96,10 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
let keyLimit = referencedTable.map { "\($0).limit" } ?? "limit"

mutableState.withValue {
if let index = $0.request.query.firstIndex(where: { $0.name == keyOffset }) {
$0.request.query[index] = URLQueryItem(name: keyOffset, value: "\(from)")
} else {
$0.request.query.append(URLQueryItem(name: keyOffset, value: "\(from)"))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: keyOffset, value: "\(from)"))

// Range is inclusive, so add 1
if let index = $0.request.query.firstIndex(where: { $0.name == keyLimit }) {
$0.request.query[index] = URLQueryItem(
name: keyLimit,
value: "\(to - from + 1)"
)
} else {
$0.request.query.append(URLQueryItem(
name: keyLimit,
value: "\(to - from + 1)"
))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: keyLimit, value: "\(to - from + 1)"))
}

return self
Expand Down Expand Up @@ -195,7 +164,8 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
.compactMap { $0 }
.joined(separator: "|")
let forMediaType = $0.request.headers[.accept] ?? "application/json"
$0.request.headers[.accept] = "application/vnd.pgrst.plan+\"\(format)\"; for=\(forMediaType); options=\(options);"
$0.request.headers[.accept] =
"application/vnd.pgrst.plan+\"\(format)\"; for=\(forMediaType); options=\(options);"
}

return self
Expand Down
17 changes: 6 additions & 11 deletions Sources/PostgREST/URLQueryRepresentable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,15 @@ extension Optional: URLQueryRepresentable where Wrapped: URLQueryRepresentable {
extension JSONObject: URLQueryRepresentable {
public var queryValue: String {
let value = mapValues(\.value)
return JSONSerialization.stringfy(value)
return JSONSerialization.stringfy(value)!
}
}

extension JSONSerialization {
static func stringfy(_ object: Any) -> String {
guard
let data = try? data(
withJSONObject: object, options: [.withoutEscapingSlashes, .sortedKeys]
),
let string = String(data: data, encoding: .utf8)
else {
return "{}"
}
return string
static func stringfy(_ object: Any) -> String? {
let data = try? data(
withJSONObject: object, options: [.withoutEscapingSlashes, .sortedKeys]
)
return data.flatMap { String(data: $0, encoding: .utf8) }
}
}
Loading
Loading