Skip to content
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
6 changes: 3 additions & 3 deletions Sources/App/Commands/Analyze.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ extension Analyze {

try await RecentPackage.refresh(on: database)
try await RecentRelease.refresh(on: database)
try await Search.refresh(on: database).get()
try await Stats.refresh(on: database).get()
try await WeightedKeyword.refresh(on: database).get()
try await Search.refresh(on: database)
try await Stats.refresh(on: database)
try await WeightedKeyword.refresh(on: database)
}


Expand Down
16 changes: 7 additions & 9 deletions Sources/App/Commands/CreateRestfile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,24 @@ enum Variant: String, LosslessStringConvertible {
}


struct CreateRestfileCommand: Command {
struct CreateRestfileCommand: AsyncCommand {
struct Signature: CommandSignature {
@Argument(name: "variant")
var variant: Variant
}

var help: String { "Create restfile for automated testing" }

func run(using context: CommandContext, signature: Signature) throws {
func run(using context: CommandContext, signature: Signature) async throws {
guard let db = context.application.db as? SQLDatabase else {
fatalError("Database must be an SQLDatabase ('as? SQLDatabase' must succeed)")
}
try createRestfile(on: db, variant: signature.variant).wait()
try await createRestfile(on: db, variant: signature.variant)
}
}


func createRestfile(on database: SQLDatabase, variant: Variant) -> EventLoopFuture<Void> {
func createRestfile(on database: SQLDatabase, variant: Variant) async throws {
let mode: String
let query: SQLQueryString
switch variant {
Expand Down Expand Up @@ -96,14 +96,12 @@ func createRestfile(on database: SQLDatabase, variant: Variant) -> EventLoopFutu
print("# auto-generated via `Run create-restfile \(variant.rawValue)`")
print("mode: \(mode)")
print("requests:")
return database.raw(query)
.all(decoding: Record.self)
.mapEach { r in
print("""
for r in try await database.raw(query).all(decoding: Record.self) {
print("""
\(r.url):
url: ${base_url}\(r.url)
validation:
status: .regex((2|3)\\d\\d)
""")
}.transform(to: ())
}
}
4 changes: 1 addition & 3 deletions Sources/App/Commands/ReAnalyzeVersions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ enum ReAnalyzeVersions {
}
}
do {
try await AppMetrics.push(client: client,
jobName: "re-analyze-versions")
.get()
try await AppMetrics.push(client: client, jobName: "re-analyze-versions")
} catch {
Current.logger().warning("\(error.localizedDescription)")
}
Expand Down
42 changes: 10 additions & 32 deletions Sources/App/Core/AppMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,45 +169,23 @@ extension AppMetrics {
/// a `/metrics` endpoint that could be scraped. Instead, they push to a gateway that is configured as a Prometheus
/// scrape target.
/// - Parameter client: client for POST request
/// - Returns: future
static func push(client: Client, jobName: String) -> EventLoopFuture<Void> {
static func push(client: Client, jobName: String) async throws {
guard let pushGatewayUrl = Current.metricsPushGatewayUrl() else {
return client.eventLoop.future(error: AppError.envVariableNotSet("METRICS_PUSHGATEWAY_URL"))
throw AppError.envVariableNotSet("METRICS_PUSHGATEWAY_URL")
}
let url = URI(string: "\(pushGatewayUrl)/metrics/job/\(jobName)")

let promise = client.eventLoop.makePromise(of: String.self)
do {
try MetricsSystem.prometheus().collect(into: promise)
let metrics: String = try await MetricsSystem.prometheus().collect()
_ = try await client.post(url) { req in
// append "\n" to avoid
// text format parsing error in line 4: unexpected end of input stream
try req.content.encode(metrics + "\n")
}
} catch {
return client.eventLoop.future(error: error)
Current.logger().warning("AppMetrics.push failed with error: \(error)")
// ignore error - we don't want metrics issues to cause upstream failures
}

let req = promise.futureResult
.flatMap { metrics in
client.post(url) { req in
// append "\n" to avoid
// text format parsing error in line 4: unexpected end of input stream
try req.content.encode(metrics + "\n")
}
}
.transform(to: ())

return req
.flatMapError { error in
Current.logger().warning("AppMetrics.push failed with error: \(error)")
// absorb error - we don't want metrics issues to cause upstream failures
return client.eventLoop.future()
}
}


/// Async-await wrapper for `EventLoopFuture`-based `push`
/// - Parameters:
/// - client: `Client`
/// - jobName: job name
static func push(client: Client, jobName: String) async throws {
try await push(client: client, jobName: jobName).get()
}

}
Expand Down
4 changes: 2 additions & 2 deletions Sources/App/Core/Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,10 @@ enum Search {
}
}

static func refresh(on database: Database) -> EventLoopFuture<Void> {
static func refresh(on database: Database) async throws {
guard let db = database as? SQLDatabase else {
fatalError("Database must be an SQLDatabase ('as? SQLDatabase' must succeed)")
}
return db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
try await db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
}
}
4 changes: 2 additions & 2 deletions Sources/App/Core/Stats.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ struct Stats: Decodable, Equatable {
}

extension Stats {
static func refresh(on database: Database) -> EventLoopFuture<Void> {
static func refresh(on database: Database) async throws {
guard let db = database as? SQLDatabase else {
fatalError("Database must be an SQLDatabase ('as? SQLDatabase' must succeed)")
}
return db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
try await db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
}


Expand Down
4 changes: 2 additions & 2 deletions Sources/App/Models/WeightedKeyword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ extension WeightedKeyword {
.all(decoding: Self.self)
}

static func refresh(on database: Database) -> EventLoopFuture<Void> {
static func refresh(on database: Database) async throws {
guard let db = database as? SQLDatabase else {
fatalError("Database must be an SQLDatabase ('as? SQLDatabase' must succeed)")
}
return db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
try await db.raw("REFRESH MATERIALIZED VIEW \(ident: Self.schema)").run()
}
}

Expand Down
6 changes: 2 additions & 4 deletions Sources/App/routes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,8 @@ func routes(_ app: Application) throws {
}

do { // Metrics
app.get("metrics") { req -> EventLoopFuture<String> in
let promise = req.eventLoop.makePromise(of: String.self)
try MetricsSystem.prometheus().collect(into: promise)
return promise.futureResult
app.get("metrics") { req -> String in
try await MetricsSystem.prometheus().collect()
}.excludeFromOpenAPI()
}
}
60 changes: 30 additions & 30 deletions Tests/AppTests/ApiTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ApiTests: AppTestCase {
summary: "foo bar package").save(on: app.db)
try await Version(package: p1, packageName: "Foo", reference: .branch("main")).save(on: app.db)
try await Version(package: p2, packageName: "Bar", reference: .branch("main")).save(on: app.db)
try await Search.refresh(on: app.db).get()
try await Search.refresh(on: app.db)

let event = ActorIsolated<TestEvent?>(nil)
Current.postPlausibleEvent = { @Sendable _, kind, path, _ in
Expand Down Expand Up @@ -805,7 +805,7 @@ class ApiTests: AppTestCase {
try await v.save(on: app.db)
try await Product(version: v, type: .library(.automatic), name: "lib")
.save(on: app.db)
try await Search.refresh(on: app.db).get()
try await Search.refresh(on: app.db)

let event = ActorIsolated<TestEvent?>(nil)
Current.postPlausibleEvent = { @Sendable _, kind, path, _ in
Expand Down Expand Up @@ -850,47 +850,47 @@ class ApiTests: AppTestCase {
}
}

func test_package_collections_packageURLs() throws {
func test_package_collections_packageURLs() async throws {
try XCTSkipIf(!isRunningInCI && Current.collectionSigningPrivateKey() == nil, "Skip test for local user due to unset COLLECTION_SIGNING_PRIVATE_KEY env variable")
// setup
let refDate = Date(timeIntervalSince1970: 0)
Current.date = { refDate }
Current.apiSigningKey = { "secret" }
let p1 = Package(id: UUID(uuidString: "442cf59f-0135-4d08-be00-bc9a7cebabd3")!,
url: "1")
try p1.save(on: app.db).wait()
try await p1.save(on: app.db)
let p2 = Package(id: UUID(uuidString: "4e256250-d1ea-4cdd-9fe9-0fc5dce17a80")!,
url: "2")
try p2.save(on: app.db).wait()
try Repository(package: p1,
defaultBranch: "main",
summary: "some package").save(on: app.db).wait()
try Repository(package: p2,
defaultBranch: "main",
name: "name 2",
owner: "foo",
summary: "foo bar package").save(on: app.db).wait()
try await p2.save(on: app.db)
try await Repository(package: p1,
defaultBranch: "main",
summary: "some package").save(on: app.db)
try await Repository(package: p2,
defaultBranch: "main",
name: "name 2",
owner: "foo",
summary: "foo bar package").save(on: app.db)
do {
let v = try Version(package: p1,
latest: .release,
packageName: "Foo",
reference: .tag(1, 2, 3),
toolsVersion: "5.3")
try v.save(on: app.db).wait()
try Product(version: v, type: .library(.automatic), name: "p1")
.save(on: app.db).wait()
try await v.save(on: app.db)
try await Product(version: v, type: .library(.automatic), name: "p1")
.save(on: app.db)
}
do {
let v = try Version(package: p2,
latest: .release,
packageName: "Bar",
reference: .tag(2, 0, 0),
toolsVersion: "5.4")
try v.save(on: app.db).wait()
try Product(version: v, type: .library(.automatic), name: "p2")
.save(on: app.db).wait()
try await v.save(on: app.db)
try await Product(version: v, type: .library(.automatic), name: "p2")
.save(on: app.db)
}
try Search.refresh(on: app.db).wait()
try await Search.refresh(on: app.db)

do { // MUT
let body: ByteBuffer = .init(string: """
Expand All @@ -913,16 +913,16 @@ class ApiTests: AppTestCase {
}
""")

try app.test(.POST,
"api/package-collections",
headers: .bearerApplicationJSON((try .apiToken(secretKey: "secret", tier: .tier3))),
body: body,
afterResponse: { res in
// validation
XCTAssertEqual(res.status, .ok)
let pkgColl = try res.content.decode(PackageCollection.self)
assertSnapshot(of: pkgColl, as: .dump)
})
try await app.test(.POST,
"api/package-collections",
headers: .bearerApplicationJSON((try .apiToken(secretKey: "secret", tier: .tier3))),
body: body,
afterResponse: { res async throws in
// validation
XCTAssertEqual(res.status, .ok)
let pkgColl = try res.content.decode(PackageCollection.self)
assertSnapshot(of: pkgColl, as: .dump)
})
}
}

Expand Down
Loading
Loading