Skip to content

Commit 9560f38

Browse files
committed
Move mastodonPost to httpClient
1 parent a8e8419 commit 9560f38

File tree

11 files changed

+104
-27
lines changed

11 files changed

+104
-27
lines changed

Sources/App/Core/Dependencies/EnvironmentClient.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ struct EnvironmentClient {
4242
var currentReferenceCache: @Sendable () -> CurrentReferenceCache?
4343
var dbId: @Sendable () -> String?
4444
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
45-
#warning("drop client parameter and move this to httpClient")
46-
var mastodonPost: @Sendable (_ client: Client, _ message: String) async throws -> Void
4745
var random: @Sendable (_ range: ClosedRange<Double>) -> Double = { XCTFail("random"); return Double.random(in: $0) }
4846

4947
enum FailureMode: String {
@@ -110,7 +108,6 @@ extension EnvironmentClient: DependencyKey {
110108
Environment.get("MASTODON_ACCESS_TOKEN")
111109
.map(Mastodon.Credentials.init(accessToken:))
112110
},
113-
mastodonPost: { client, message in try await Mastodon.post(client: client, message: message) },
114111
random: { range in Double.random(in: range) },
115112
shouldFail: { failureMode in
116113
let shouldFail = Environment.get("FAILURE_MODE")

Sources/App/Core/Dependencies/HTTPClient.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ struct HTTPClient {
2323
typealias Request = Vapor.HTTPClient.Request
2424
typealias Response = Vapor.HTTPClient.Response
2525

26-
var post: @Sendable (_ url: String, _ headers: HTTPHeaders, _ body: Data) async throws -> Response
26+
var post: @Sendable (_ url: String, _ headers: HTTPHeaders, _ body: Data?) async throws -> Response
2727
var fetchDocumentation: @Sendable (_ url: URI) async throws -> Response
2828
var fetchHTTPStatusCode: @Sendable (_ url: String) async throws -> HTTPStatus
29+
var mastodonPost: @Sendable (_ message: String) async throws -> Void
2930
var postPlausibleEvent: @Sendable (_ kind: Plausible.Event.Kind, _ path: Plausible.Path, _ user: User?) async throws -> Void
3031
}
3132

3233
extension HTTPClient: DependencyKey {
3334
static var liveValue: HTTPClient {
3435
.init(
3536
post: { url, headers, body in
36-
let req = try Request(url: url, method: .POST, headers: headers, body: .data(body))
37+
let req = try Request(url: url, method: .POST, headers: headers, body: body.map({.data($0)}))
3738
return try await Vapor.HTTPClient.shared.execute(request: req).get()
3839
},
3940
fetchDocumentation: { url in
@@ -53,6 +54,7 @@ extension HTTPClient: DependencyKey {
5354
try await client.shutdown()
5455
}
5556
},
57+
mastodonPost: { message in try await Mastodon.post(message: message) },
5658
postPlausibleEvent: { kind, path, user in
5759
try await Plausible.postEvent(kind: kind, path: path, user: user)
5860
}

Sources/App/Core/Mastodon.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,33 @@ enum Mastodon {
2626
var accessToken: String
2727
}
2828

29-
// NB: _testEncodedURL is a callback that exists purely to be able to regression test the encoded value
30-
static func post(client: Client, message: String, _testEncodedURL: (String) -> Void = { _ in }) async throws {
29+
static func post(message: String) async throws {
3130
@Dependency(\.environment) var environment
31+
@Dependency(\.httpClient) var httpClient
32+
@Dependency(\.uuid) var uuid
3233
guard let credentials = environment.mastodonCredentials() else {
3334
throw Social.Error.missingCredentials
3435
}
3536

3637
let headers = HTTPHeaders([
3738
("Authorization", "Bearer \(credentials.accessToken)"),
38-
("Idempotency-Key", UUID().uuidString),
39+
("Idempotency-Key", uuid().uuidString),
3940
])
4041

4142
struct Query: Encodable {
4243
var status: String
4344
}
4445

45-
let res = try await client.post(URI(string: apiURL), headers: headers) { req in
46-
try req.query.encode(Query(status: message))
47-
_testEncodedURL(req.url.string)
46+
var components = URLComponents()
47+
components.scheme = "https"
48+
components.host = Mastodon.instance
49+
components.path = "/api/v1/statuses"
50+
components.queryItems = [URLQueryItem(name: "status", value: message)]
51+
guard let url = components.string else {
52+
throw Social.Error.invalidURL
4853
}
54+
let res = try await httpClient.post(url: url, headers: headers, body: nil)
55+
4956
guard res.status == .ok else {
5057
throw Social.Error.requestFailed(res.status, res.body?.asString() ?? "")
5158
}

Sources/App/Core/Social.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum Social {
2222

2323
enum Error: LocalizedError {
2424
case invalidMessage
25+
case invalidURL
2526
case missingCredentials
2627
case postingDisabled
2728
case requestFailed(HTTPStatus, String)
@@ -98,14 +99,15 @@ enum Social {
9899
package: Joined<Package, Repository>,
99100
version: Version) async throws {
100101
@Dependency(\.environment) var environment
102+
@Dependency(\.httpClient) var httpClient
101103
guard environment.allowSocialPosts() else { throw Error.postingDisabled }
102104
guard let message = firehoseMessage(package: package,
103105
version: version,
104106
maxLength: postMaxLength) else {
105107
throw Error.invalidMessage
106108
}
107109
// Ignore errors from here for now to keep concurrency simpler
108-
async let _ = try? await environment.mastodonPost(client: client, message: message)
110+
async let _ = try? await httpClient.mastodonPost(message: message)
109111
}
110112

111113
static func postToFirehose(client: Client,

Tests/AppTests/AnalyzeErrorTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ final class AnalyzeErrorTests: AppTestCase {
104104
withDependencies {
105105
$0.date.now = .t0
106106
$0.environment.allowSocialPosts = { true }
107-
$0.environment.mastodonPost = { @Sendable [socialPosts = self.socialPosts] _, message in
107+
$0.httpClient.mastodonPost = { @Sendable [socialPosts = self.socialPosts] message in
108108
socialPosts.withValue { $0.append(message) }
109109
}
110110
} operation: {

Tests/AppTests/AnalyzerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class AnalyzerTests: AppTestCase {
3636
try await withDependencies {
3737
$0.date.now = .now
3838
$0.environment.allowSocialPosts = { true }
39-
$0.environment.mastodonPost = { @Sendable _, _ in }
39+
$0.httpClient.mastodonPost = { @Sendable _ in }
4040
} operation: {
4141
// setup
4242
let urls = ["https://github.com/foo/1", "https://github.com/foo/2"]
@@ -219,7 +219,7 @@ class AnalyzerTests: AppTestCase {
219219
try await withDependencies {
220220
$0.date.now = .now
221221
$0.environment.allowSocialPosts = { true }
222-
$0.environment.mastodonPost = { @Sendable _, _ in }
222+
$0.httpClient.mastodonPost = { @Sendable _ in }
223223
} operation: {
224224
// setup
225225
let pkgId = UUID()

Tests/AppTests/LiveTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
@testable import App
16+
17+
import Dependencies
18+
import XCTVapor
19+
20+
21+
class LiveTests: XCTestCase {
22+
23+
func test_Mastodon_post() async throws {
24+
// Only run this test manually to confirm posting works
25+
try XCTSkipIf(true)
26+
27+
try await withDependencies {
28+
$0.environment.mastodonCredentials = { .dev }
29+
$0.httpClient = .liveValue
30+
$0.uuid = .init(UUID.init)
31+
} operation: {
32+
let message = Social.versionUpdateMessage(
33+
packageName: "packageName",
34+
repositoryOwnerName: "owner",
35+
url: "http://localhost:8080/owner/SomePackage",
36+
version: .init(2, 6, 4),
37+
summary: "Testing, testing αβγ ÄÖÜß 🔤 ✅",
38+
maxLength: Social.postMaxLength
39+
)
40+
41+
try await Mastodon.post(message: message)
42+
}
43+
}
44+
45+
}
46+
47+
48+
extension Mastodon.Credentials {
49+
// https://mas.to/@spi_test
50+
static var dev: Self? {
51+
guard let accessToken = Environment.get("DEV_MASTODON_ACCESS_TOKEN") else { return nil }
52+
return .init(accessToken: accessToken)
53+
}
54+
}

Tests/AppTests/MastodonTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class MastodonTests: AppTestCase {
2525
let message = QueueIsolated<String?>(nil)
2626
try await withDependencies {
2727
$0.environment.allowSocialPosts = { true }
28-
$0.environment.mastodonPost = { @Sendable _, msg in
28+
$0.httpClient.mastodonPost = { @Sendable msg in
2929
if message.value == nil {
3030
message.setValue(msg)
3131
} else {

Tests/AppTests/PlausibleTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ final class PlausibleTests: XCTestCase {
3636
$0.httpClient.post = { @Sendable _, _, body in
3737
await called.withValue { $0 = true }
3838
// validate
39+
let body = try XCTUnwrap(body)
3940
XCTAssertEqual(try? JSONDecoder().decode(Plausible.Event.self, from: body),
4041
.init(name: .pageview,
4142
url: "https://foo.bar/api/search",
@@ -59,6 +60,7 @@ final class PlausibleTests: XCTestCase {
5960
$0.httpClient.post = { @Sendable _, _, body in
6061
await called.withValue { $0 = true }
6162
// validate
63+
let body = try XCTUnwrap(body)
6264
XCTAssertEqual(try? JSONDecoder().decode(Plausible.Event.self, from: body),
6365
.init(name: .pageview,
6466
url: "https://foo.bar/api/packages/{owner}/{repository}",

Tests/AppTests/ReAnalyzeVersionsTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ReAnalyzeVersionsTests: AppTestCase {
2929
try await withDependencies {
3030
$0.date.now = .t0
3131
$0.environment.allowSocialPosts = { true }
32-
$0.environment.mastodonPost = { @Sendable _, _ in }
32+
$0.httpClient.mastodonPost = { @Sendable _ in }
3333
} operation: {
3434
// setup
3535
// - package dump does not include toolsVersion, targets to simulate an "old version"

0 commit comments

Comments
 (0)