Skip to content

Commit 7df64b8

Browse files
Merge pull request #3562 from SwiftPackageIndex/issue-3469-dependency-transition-11
Issue 3469 dependency transition 11
2 parents 4832feb + 806015b commit 7df64b8

File tree

11 files changed

+238
-211
lines changed

11 files changed

+238
-211
lines changed

Sources/App/Core/AppEnvironment.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ struct AppEnvironment: Sendable {
4141
var logger: @Sendable () -> Logger
4242
var metricsPushGatewayUrl: @Sendable () -> String?
4343
var plausibleBackendReportingSiteID: @Sendable () -> String?
44-
var postPlausibleEvent: @Sendable (Client, Plausible.Event.Kind, Plausible.Path, User?) async throws -> Void
4544
var processingBuildBacklog: @Sendable () -> Bool
4645
var runnerIds: @Sendable () -> [String]
4746
var setHTTPClient: @Sendable (Client) -> Void
@@ -65,19 +64,6 @@ struct AppEnvironment: Sendable {
6564
}
6665

6766

68-
extension AppEnvironment {
69-
func postPlausibleEvent(_ event: Plausible.Event.Kind, path: Plausible.Path, user: User?) {
70-
Task {
71-
do {
72-
try await Current.postPlausibleEvent(Current.httpClient(), event, path, user)
73-
} catch {
74-
Current.logger().warning("Plausible.postEvent failed: \(error)")
75-
}
76-
}
77-
}
78-
}
79-
80-
8167
extension AppEnvironment {
8268
nonisolated(unsafe) static var httpClient: Client!
8369
nonisolated(unsafe) static var logger: Logger!
@@ -115,7 +101,6 @@ extension AppEnvironment {
115101
logger: { logger },
116102
metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") },
117103
plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") },
118-
postPlausibleEvent: { client, kind, path, user in try await Plausible.postEvent(client: client, kind: kind, path: path, user: user) },
119104
processingBuildBacklog: {
120105
Environment.get("PROCESSING_BUILD_BACKLOG").flatMap(\.asBool) ?? false
121106
},

Sources/App/Core/BackendReportingMiddleware.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Dependencies
1516
import Vapor
1617

1718

@@ -23,7 +24,8 @@ struct BackendReportingMiddleware: AsyncMiddleware {
2324
let response = try await next.respond(to: request)
2425
guard isActive else { return response }
2526
let user = try? request.auth.require(User.self)
26-
Current.postPlausibleEvent(.pageview, path: path, user: user)
27+
@Dependency(\.httpClient) var httpClient
28+
try await httpClient.postPlausibleEvent(kind: .pageview, path: path, user: user)
2729
return response
2830
}
2931
}

Sources/App/Core/Dependencies/EnvironmentClient.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import Vapor
2121
struct EnvironmentClient {
2222
// See https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
2323
// regarding the use of XCTFail here.
24-
// Closures returning optionals or Void don't need this, because they automatically get the default failing
24+
// Closures that are throwing or return Void don't need this, because they automatically get the default failing
2525
// mechanism when they're not set up in a test.
2626
var allowBuildTriggers: @Sendable () -> Bool = { XCTFail("allowBuildTriggers"); return true }
2727
var allowSocialPosts: @Sendable () -> Bool = { XCTFail("allowSocialPosts"); return true }
@@ -42,7 +42,8 @@ struct EnvironmentClient {
4242
var currentReferenceCache: @Sendable () -> CurrentReferenceCache?
4343
var dbId: @Sendable () -> String?
4444
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
45-
var mastodonPost: @Sendable (_ client: Client, _ post: String) async throws -> Void
45+
#warning("drop client parameter and move this to httpClient")
46+
var mastodonPost: @Sendable (_ client: Client, _ message: String) async throws -> Void
4647
var random: @Sendable (_ range: ClosedRange<Double>) -> Double = { XCTFail("random"); return Double.random(in: $0) }
4748

4849
enum FailureMode: String {

Sources/App/Core/Dependencies/HTTPClient.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,22 @@ import Vapor
2020

2121
@DependencyClient
2222
struct HTTPClient {
23+
typealias Request = Vapor.HTTPClient.Request
2324
typealias Response = Vapor.HTTPClient.Response
2425

26+
var post: @Sendable (_ url: String, _ headers: HTTPHeaders, _ body: Data) async throws -> Response
2527
var fetchDocumentation: @Sendable (_ url: URI) async throws -> Response
2628
var fetchHTTPStatusCode: @Sendable (_ url: String) async throws -> HTTPStatus
29+
var postPlausibleEvent: @Sendable (_ kind: Plausible.Event.Kind, _ path: Plausible.Path, _ user: User?) async throws -> Void
2730
}
2831

2932
extension HTTPClient: DependencyKey {
3033
static var liveValue: HTTPClient {
3134
.init(
35+
post: { url, headers, body in
36+
let req = try Request(url: url, method: .POST, headers: headers, body: .data(body))
37+
return try await Vapor.HTTPClient.shared.execute(request: req).get()
38+
},
3239
fetchDocumentation: { url in
3340
try await Vapor.HTTPClient.shared.get(url: url.string).get()
3441
},
@@ -45,6 +52,9 @@ extension HTTPClient: DependencyKey {
4552
} defer: {
4653
try await client.shutdown()
4754
}
55+
},
56+
postPlausibleEvent: { kind, path, user in
57+
try await Plausible.postEvent(kind: kind, path: path, user: user)
4858
}
4959
)
5060
}
@@ -74,6 +84,10 @@ extension HTTPClient {
7484
.init(status: .ok, headers: headers, body: .init(string: url.path))
7585
}
7686
}
87+
88+
static var noop: @Sendable (_ kind: Plausible.Event.Kind, _ path: Plausible.Path, _ user: User?) async throws -> Void {
89+
{ _, _, _ in }
90+
}
7791
}
7892

7993
extension HTTPClient.Response {

Sources/App/Core/Plausible.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
import Vapor
16+
import Dependencies
1617

1718

1819
enum Plausible {
@@ -43,18 +44,18 @@ enum Plausible {
4344
var message: String
4445
}
4546

46-
static let postEventURI = URI(string: "https://plausible.io/api/event")
47+
static let postEventURL = "https://plausible.io/api/event"
4748

48-
static func postEvent(client: Client, kind: Event.Kind, path: Path, user: User?) async throws {
49+
static func postEvent(kind: Event.Kind, path: Path, user: User?) async throws {
4950
guard let siteID = Current.plausibleBackendReportingSiteID() else {
5051
throw Error(message: "PLAUSIBLE_BACKEND_REPORTING_SITE_ID not set")
5152
}
52-
let res = try await client.post(postEventURI, headers: .applicationJSON) { req in
53-
try req.content.encode(Event(name: .pageview,
54-
url: "https://\(siteID)\(path.rawValue)",
55-
domain: siteID,
56-
props: user.props))
57-
}
53+
let body = try JSONEncoder().encode(Event(name: .pageview,
54+
url: "https://\(siteID)\(path.rawValue)",
55+
domain: siteID,
56+
props: user.props))
57+
@Dependency(\.httpClient) var httpClient
58+
let res = try await httpClient.post(url: postEventURL, headers: .applicationJSON, body: body)
5859
guard res.status.succeeded else {
5960
throw Error(message: "Request failed with status code: \(res.status)")
6061
}

Sources/App/Core/Social.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ enum Social {
105105
throw Error.invalidMessage
106106
}
107107
// Ignore errors from here for now to keep concurrency simpler
108-
async let _ = try? await environment.mastodonPost(client, message)
108+
async let _ = try? await environment.mastodonPost(client: client, message: message)
109109
}
110110

111111
static func postToFirehose(client: Client,

Tests/AppTests/ApiTests.swift

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ApiTests: AppTestCase {
3232
func test_search_noQuery() throws {
3333
try withDependencies {
3434
$0.environment.apiSigningKey = { "secret" }
35+
$0.httpClient.postPlausibleEvent = App.HTTPClient.noop
3536
} operation: {
3637
// MUT
3738
try app.test(.GET, "api/search",
@@ -48,8 +49,12 @@ class ApiTests: AppTestCase {
4849
}
4950

5051
func test_search_basic_param() async throws {
52+
let event = App.ActorIsolated<TestEvent?>(nil)
5153
try await withDependencies {
5254
$0.environment.apiSigningKey = { "secret" }
55+
$0.httpClient.postPlausibleEvent = { @Sendable kind, path, _ in
56+
await event.setValue(.init(kind: kind, path: path))
57+
}
5358
} operation: {
5459
let p1 = Package(id: .id0, url: "1")
5560
try await p1.save(on: app.db)
@@ -69,11 +74,6 @@ class ApiTests: AppTestCase {
6974
try await Version(package: p2, packageName: "Bar", reference: .branch("main")).save(on: app.db)
7075
try await Search.refresh(on: app.db)
7176

72-
let event = App.ActorIsolated<TestEvent?>(nil)
73-
Current.postPlausibleEvent = { @Sendable _, kind, path, _ in
74-
await event.setValue(.init(kind: kind, path: path))
75-
}
76-
7777
// MUT
7878
try await app.test(.GET, "api/search?query=foo%20bar",
7979
headers: .bearerApplicationJSON(try .apiToken(secretKey: "secret", tier: .tier1)),
@@ -765,6 +765,7 @@ class ApiTests: AppTestCase {
765765
}
766766

767767
func test_get_badge() async throws {
768+
// sas 2024-12-20: Badges are not reporting plausbile events, because they triggered way too many events. (This is an old changes, just adding this comment today as I'm removing the old, commented out test remnants we still had in place.)
768769
// setup
769770
let owner = "owner"
770771
let repo = "repo"
@@ -782,11 +783,6 @@ class ApiTests: AppTestCase {
782783
try await Build(version: v, platform: .macosXcodebuild, status: .ok, swiftVersion: .v1)
783784
.save(on: app.db)
784785

785-
let event = App.ActorIsolated<TestEvent?>(nil)
786-
Current.postPlausibleEvent = { @Sendable _, kind, path, _ in
787-
await event.setValue(.init(kind: kind, path: path))
788-
}
789-
790786
// MUT - swift versions
791787
try await app.test(
792788
.GET,
@@ -822,21 +818,20 @@ class ApiTests: AppTestCase {
822818
XCTAssertEqual(badge.cacheSeconds, 6*3600)
823819
XCTAssertNotNil(badge.logoSvg)
824820
})
825-
826-
// ensure API event has been reported
827-
// API reporting for badges is currently disabled, because it is very noisy
828-
// await event.withValue {
829-
// XCTAssertEqual($0, .some(.init(kind: .pageview, path: .badge)))
830-
// }
831821
}
832822

833823
func test_package_collections_owner() async throws {
834824
try XCTSkipIf(!isRunningInCI && EnvironmentClient.liveValue.collectionSigningPrivateKey() == nil, "Skip test for local user due to unset COLLECTION_SIGNING_PRIVATE_KEY env variable")
825+
826+
let event = App.ActorIsolated<TestEvent?>(nil)
835827
try await withDependencies {
836828
$0.date.now = .t0
837829
$0.environment.apiSigningKey = { "secret" }
838830
$0.environment.collectionSigningCertificateChain = EnvironmentClient.liveValue.collectionSigningCertificateChain
839831
$0.environment.collectionSigningPrivateKey = EnvironmentClient.liveValue.collectionSigningPrivateKey
832+
$0.httpClient.postPlausibleEvent = { @Sendable kind, path, _ in
833+
await event.setValue(.init(kind: kind, path: path))
834+
}
840835
} operation: {
841836
// setup
842837
let p1 = Package(id: .id1, url: "1")
@@ -855,11 +850,6 @@ class ApiTests: AppTestCase {
855850
try await Product(version: v, type: .library(.automatic), name: "lib")
856851
.save(on: app.db)
857852

858-
let event = App.ActorIsolated<TestEvent?>(nil)
859-
Current.postPlausibleEvent = { @Sendable _, kind, path, _ in
860-
await event.setValue(.init(kind: kind, path: path))
861-
}
862-
863853
do { // MUT
864854
let body: ByteBuffer = .init(string: """
865855
{
@@ -901,12 +891,14 @@ class ApiTests: AppTestCase {
901891

902892
func test_package_collections_packageURLs() async throws {
903893
try XCTSkipIf(!isRunningInCI && EnvironmentClient.liveValue.collectionSigningPrivateKey() == nil, "Skip test for local user due to unset COLLECTION_SIGNING_PRIVATE_KEY env variable")
894+
904895
let refDate = Date(timeIntervalSince1970: 0)
905896
try await withDependencies {
906897
$0.date.now = refDate
907898
$0.environment.apiSigningKey = { "secret" }
908899
$0.environment.collectionSigningCertificateChain = EnvironmentClient.liveValue.collectionSigningCertificateChain
909900
$0.environment.collectionSigningPrivateKey = EnvironmentClient.liveValue.collectionSigningPrivateKey
901+
$0.httpClient.postPlausibleEvent = App.HTTPClient.noop
910902
} operation: {
911903
// setup
912904
let p1 = Package(id: UUID(uuidString: "442cf59f-0135-4d08-be00-bc9a7cebabd3")!,
@@ -1049,6 +1041,7 @@ class ApiTests: AppTestCase {
10491041
try await withDependencies {
10501042
$0.environment.apiSigningKey = { "secret" }
10511043
$0.environment.dbId = { nil }
1044+
$0.httpClient.postPlausibleEvent = App.HTTPClient.noop
10521045
} operation: {
10531046
let owner = "owner"
10541047
let repo = "repo"
@@ -1112,6 +1105,7 @@ class ApiTests: AppTestCase {
11121105
func test_dependencies_get() async throws {
11131106
try await withDependencies {
11141107
$0.environment.apiSigningKey = { "secret" }
1108+
$0.httpClient.postPlausibleEvent = App.HTTPClient.noop
11151109
} operation: {
11161110
let pkg = try await savePackage(on: app.db, id: .id0, "http://github.com/foo/bar")
11171111
try await Repository(package: pkg,

Tests/AppTests/Mocks/AppEnvironment+mock.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ extension AppEnvironment {
4040
logger: { logger },
4141
metricsPushGatewayUrl: { "http://pushgateway:9091" },
4242
plausibleBackendReportingSiteID: { nil },
43-
postPlausibleEvent: { _, _, _, _ in },
4443
processingBuildBacklog: { false },
4544
runnerIds: { [] },
4645
setHTTPClient: { client in Self.httpClient = client },

Tests/AppTests/PlausibleTests.swift

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import XCTest
1616

1717
@testable import App
1818

19+
import Dependencies
20+
1921

2022
final class PlausibleTests: XCTestCase {
2123

@@ -29,43 +31,49 @@ final class PlausibleTests: XCTestCase {
2931
}
3032

3133
func test_postEvent_anonymous() async throws {
32-
Current.plausibleBackendReportingSiteID = { "foo.bar" }
33-
34-
var called = false
35-
let client = MockClient { req, _ in
36-
called = true
37-
// validate
38-
XCTAssertEqual(try? req.content.decode(Plausible.Event.self),
39-
.init(name: .pageview,
40-
url: "https://foo.bar/api/search",
41-
domain: "foo.bar",
42-
props: ["user": "none"]))
43-
}
34+
let called = ActorIsolated(false)
35+
try await withDependencies {
36+
$0.httpClient.post = { @Sendable _, _, body in
37+
await called.withValue { $0 = true }
38+
// validate
39+
XCTAssertEqual(try? JSONDecoder().decode(Plausible.Event.self, from: body),
40+
.init(name: .pageview,
41+
url: "https://foo.bar/api/search",
42+
domain: "foo.bar",
43+
props: ["user": "none"]))
44+
return .ok
45+
}
46+
} operation: {
47+
Current.plausibleBackendReportingSiteID = { "foo.bar" }
4448

45-
// MUT
46-
_ = try await Plausible.postEvent(client: client, kind: .pageview, path: .search, user: nil)
49+
// MUT
50+
_ = try await Plausible.postEvent(kind: .pageview, path: .search, user: nil)
4751

48-
XCTAssertTrue(called)
52+
await called.withValue { XCTAssertTrue($0) }
53+
}
4954
}
5055

5156
func test_postEvent_package() async throws {
52-
Current.plausibleBackendReportingSiteID = { "foo.bar" }
57+
let called = ActorIsolated(false)
58+
try await withDependencies {
59+
$0.httpClient.post = { @Sendable _, _, body in
60+
await called.withValue { $0 = true }
61+
// validate
62+
XCTAssertEqual(try? JSONDecoder().decode(Plausible.Event.self, from: body),
63+
.init(name: .pageview,
64+
url: "https://foo.bar/api/packages/{owner}/{repository}",
65+
domain: "foo.bar",
66+
props: ["user": "3c469e9d"]))
67+
return .ok
68+
}
69+
} operation: {
70+
Current.plausibleBackendReportingSiteID = { "foo.bar" }
71+
let user = User(name: "api", identifier: "3c469e9d")
5372

54-
let user = User(name: "api", identifier: "3c469e9d")
55-
var called = false
56-
let client = MockClient { req, _ in
57-
called = true
58-
// validate
59-
XCTAssertEqual(try? req.content.decode(Plausible.Event.self),
60-
.init(name: .pageview,
61-
url: "https://foo.bar/api/packages/{owner}/{repository}",
62-
domain: "foo.bar",
63-
props: ["user": user.identifier]))
64-
}
65-
66-
// MUT
67-
_ = try await Plausible.postEvent(client: client, kind: .pageview, path: .package, user: user)
73+
// MUT
74+
_ = try await Plausible.postEvent(kind: .pageview, path: .package, user: user)
6875

69-
XCTAssertTrue(called)
76+
await called.withValue { XCTAssertTrue($0) }
77+
}
7078
}
7179
}

0 commit comments

Comments
 (0)