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
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ extension API {
enum PackageCollectionController {

@Sendable
static func generate(req: Request) throws -> EventLoopFuture<SignedCollection> {
static func generate(req: Request) async throws -> SignedCollection {
AppMetrics.apiPackageCollectionGetTotal?.inc()

let dto = try req.content.decode(PostPackageCollectionDTO.self)

switch dto.selection {
case let .author(author):
return SignedCollection.generate(
return try await SignedCollection.generate(
db: req.db,
filterBy: .author(author),
authorName: dto.authorName ?? "Swift Package Index",
Expand All @@ -44,7 +44,7 @@ extension API {
guard packageURLs.count <= 20 else {
throw Abort(.badRequest)
}
return SignedCollection.generate(
return try await SignedCollection.generate(
db: req.db,
filterBy: .urls(packageURLs),
authorName: dto.authorName ?? "Swift Package Index",
Expand Down
23 changes: 10 additions & 13 deletions Sources/App/Controllers/PackageCollectionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,19 @@ import Vapor

enum PackageCollectionController {
@Sendable
static func generate(req: Request) throws -> EventLoopFuture<SignedCollection> {
static func generate(req: Request) async throws -> SignedCollection {
AppMetrics.packageCollectionGetTotal?.inc()

guard let owner = req.parameters.get("owner") else {
return req.eventLoop.future(error: Abort(.notFound))
}
guard let owner = req.parameters.get("owner") else { throw Abort(.notFound) }

return SignedCollection.generate(
db: req.db,
filterBy: .author(owner),
authorName: "\(owner) via the Swift Package Index"
).flatMapError {
if case PackageCollection.Error.noResults = $0 {
return req.eventLoop.makeFailedFuture(Abort(.notFound))
}
return req.eventLoop.makeFailedFuture($0)
do {
return try await SignedCollection.generate(
db: req.db,
filterBy: .author(owner),
authorName: "\(owner) via the Swift Package Index"
)
} catch PackageCollection.Error.noResults {
throw Abort(.notFound)
}
}
}
6 changes: 3 additions & 3 deletions Sources/App/Core/PackageCollection+VersionResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ extension PackageCollection.VersionResult {
var targets: [App.Target] { version.targets }
var version: App.Version { model.version }

static func query(on database: Database, filterBy filter: PackageCollection.Filter) -> EventLoopFuture<[Self]> {
static func query(on database: Database, filterBy filter: PackageCollection.Filter) async throws -> [Self] {
let query = M
.query(
on: database,
Expand All @@ -59,8 +59,8 @@ extension PackageCollection.VersionResult {
query.filter(App.Package.self, \.$url ~~ packageURLs)
}

return query.all()
.mapEach(Self.init(model:))
return try await query.all()
.map(Self.init(model:))
}
}

Expand Down
57 changes: 24 additions & 33 deletions Sources/App/Core/PackageCollection+generate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,30 @@ extension PackageCollection {
collectionName: String? = nil,
keywords: [String]? = nil,
overview: String? = nil,
revision: Int? = nil) -> EventLoopFuture<PackageCollection> {
VersionResult.query(on: db, filterBy: filter)
.map { results in
// Multiple versions can reference the same package, therefore
// we need to group them so we don't create duplicate packages.
results.groupedByPackage(sortBy: .url)
}
.map { groups -> ([Package], String, String) in
let packages = groups.compactMap {
Package.init(resultGroup: $0,
keywords: keywords)
}
let authorLabel = authorLabel(repositories: groups.map(\.repository))
let collectionName = collectionName ?? Self.collectionName(for: filter, authorLabel: authorLabel)
let overview = overview ?? Self.overview(for: filter, authorLabel: authorLabel)
return (packages, collectionName, overview)
}
.flatMap { packages, collectionName, overview in
guard !packages.isEmpty else {
return db.eventLoop.makeFailedFuture(Error.noResults)
}
return db.eventLoop.makeSucceededFuture(
PackageCollection.init(
name: collectionName,
overview: overview,
keywords: keywords,
packages: packages,
formatVersion: .v1_0,
revision: revision,
generatedAt: Current.date(),
generatedBy: authorName.map(Author.init(name:)))
)
}
revision: Int? = nil) async throws -> PackageCollection {
let results = try await VersionResult.query(on: db, filterBy: filter)

// Multiple versions can reference the same package, therefore
// we need to group them so we don't create duplicate packages.
let groups = results.groupedByPackage(sortBy: .url)

let packages = groups.compactMap { Package.init(resultGroup: $0, keywords: keywords) }
let authorLabel = authorLabel(repositories: groups.map(\.repository))
let collectionName = collectionName ?? Self.collectionName(for: filter, authorLabel: authorLabel)
let overview = overview ?? Self.overview(for: filter, authorLabel: authorLabel)

guard !packages.isEmpty else { throw Error.noResults }

return PackageCollection.init(
name: collectionName,
overview: overview,
keywords: keywords,
packages: packages,
formatVersion: .v1_0,
revision: revision,
generatedAt: Current.date(),
generatedBy: authorName.map(Author.init(name:))
)
}

static func authorLabel(repositories: [Repository]) -> String? {
Expand Down
40 changes: 17 additions & 23 deletions Sources/App/Core/PackageCollection+signing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,30 @@ extension SignedCollection {
collectionName: String? = nil,
keywords: [String]? = nil,
overview: String? = nil,
revision: Int? = nil) -> EventLoopFuture<SignedCollection> {
PackageCollection.generate(db: db,
filterBy: filter,
authorName: authorName,
collectionName: collectionName,
keywords: keywords,
overview: overview,
revision: revision)
.flatMap {
sign(eventLoop: db.eventLoop, collection: $0)
}
revision: Int? = nil) async throws -> SignedCollection {
let collection = try await PackageCollection.generate(db: db,
filterBy: filter,
authorName: authorName,
collectionName: collectionName,
keywords: keywords,
overview: overview,
revision: revision)
return try await sign(collection: collection)
}

static func sign(eventLoop: EventLoop, collection: PackageCollection) -> EventLoopFuture<SignedCollection> {
static func sign(collection: PackageCollection) async throws -> SignedCollection {
guard let privateKey = Current.collectionSigningPrivateKey() else {
return eventLoop.makeFailedFuture(AppError.envVariableNotSet("COLLECTION_SIGNING_PRIVATE_KEY"))
throw AppError.envVariableNotSet("COLLECTION_SIGNING_PRIVATE_KEY")
}

return eventLoop.makeFutureWithTask {
try await signer.sign(collection: collection,
certChainPaths: Current.collectionSigningCertificateChain(),
privateKeyPEM: privateKey)
}
return try await signer.sign(collection: collection,
certChainPaths: Current.collectionSigningCertificateChain(),
privateKeyPEM: privateKey)
}

static func validate(eventLoop: EventLoop, signedCollection: SignedCollection) -> EventLoopFuture<Bool> {
eventLoop.makeFutureWithTask {
try await signer.validate(signedCollection: signedCollection)
return true
}
static func validate(signedCollection: SignedCollection) async throws -> Bool {
try await signer.validate(signedCollection: signedCollection)
return true
}

static let certsDir = URL(fileURLWithPath: Current.fileManager.workingDirectory())
Expand Down
Loading
Loading