Skip to content

Commit 7e4ed39

Browse files
Add forked from list item on package view
1 parent 8c68b93 commit 7e4ed39

File tree

6 files changed

+57
-5
lines changed

6 files changed

+57
-5
lines changed

Sources/App/Controllers/API/API+PackageController+GetRoute+Model.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ extension API.PackageController.GetRoute {
5050
var releaseReferences: [App.Version.Kind: App.Reference]
5151
var fundingLinks: [FundingLink]
5252
var swift6Readiness: Swift6Readiness?
53+
var forkedFromURL: String?
5354

5455
internal init(packageId: Package.Id,
5556
repositoryOwner: String,
@@ -81,7 +82,8 @@ extension API.PackageController.GetRoute {
8182
releaseReference: App.Reference?,
8283
preReleaseReference: App.Reference?,
8384
fundingLinks: [FundingLink] = [],
84-
swift6Readiness: Swift6Readiness?
85+
swift6Readiness: Swift6Readiness?,
86+
forkedFromURL: String?
8587
) {
8688
self.packageId = packageId
8789
self.repositoryOwner = repositoryOwner
@@ -123,6 +125,7 @@ extension API.PackageController.GetRoute {
123125
}()
124126
self.fundingLinks = fundingLinks
125127
self.swift6Readiness = swift6Readiness
128+
self.forkedFromURL = forkedFromURL
126129
}
127130

128131
init?(result: API.PackageController.PackageResult,
@@ -132,7 +135,8 @@ extension API.PackageController.GetRoute {
132135
swiftVersionBuildInfo: BuildInfo<CompatibilityMatrix.SwiftVersionCompatibility>?,
133136
platformBuildInfo: BuildInfo<CompatibilityMatrix.PlatformCompatibility>?,
134137
weightedKeywords: [WeightedKeyword] = [],
135-
swift6Readiness: Swift6Readiness?) {
138+
swift6Readiness: Swift6Readiness?,
139+
forkedFromURL: String?) {
136140
// we consider certain attributes as essential and return nil (raising .notFound)
137141
let repository = result.repository
138142
guard
@@ -177,7 +181,8 @@ extension API.PackageController.GetRoute {
177181
releaseReference: result.releaseVersion?.reference,
178182
preReleaseReference: result.preReleaseVersion?.reference,
179183
fundingLinks: result.repository.fundingLinks,
180-
swift6Readiness: swift6Readiness
184+
swift6Readiness: swift6Readiness,
185+
forkedFromURL: forkedFromURL
181186
)
182187

183188
}

Sources/App/Controllers/API/API+PackageController+GetRoute.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ extension API.PackageController {
3030
let packageResult = try await PackageResult.query(on: database,
3131
owner: owner,
3232
repository: repository)
33+
34+
let forkedFromURL = try await self.fetchForkedFromURL(on: database,
35+
repository: packageResult.repository)
36+
3337
async let weightedKeywords = WeightedKeyword.query(
3438
on: database, keywords: packageResult.repository.keywords
3539
)
@@ -55,7 +59,8 @@ extension API.PackageController {
5559
swiftVersionBuildInfo: buildInfo.swiftVersion,
5660
platformBuildInfo: buildInfo.platform,
5761
weightedKeywords: weightedKeywords,
58-
swift6Readiness: buildInfo.swift6Readiness
62+
swift6Readiness: buildInfo.swift6Readiness,
63+
forkedFromURL: forkedFromURL
5964
),
6065
let schema = API.PackageSchema(result: packageResult)
6166
else {
@@ -64,6 +69,20 @@ extension API.PackageController {
6469

6570
return (model, schema)
6671
}
72+
73+
private static func fetchForkedFromURL(on database: Database, repository: Repository) async throws -> String? {
74+
if let forkedFrom = repository.forkedFrom {
75+
switch forkedFrom {
76+
case .parentId(let id):
77+
let repo = try await Repository.find(on: database, for: id)
78+
guard let owner = repo?.owner, let name = repo?.name else { return nil }
79+
return SiteURL.package(.value(owner), .value(name), nil).absoluteURL()
80+
case .parentURL(let string):
81+
return string
82+
}
83+
}
84+
return nil
85+
}
6786
}
6887
}
6988

Sources/App/Controllers/API/Types+WithExample.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ extension API.PackageController.GetRoute.Model: WithExample {
247247
defaultBranchReference: .branch("main"),
248248
releaseReference: .tag(1, 2, 3, "1.2.3"),
249249
preReleaseReference: nil,
250-
swift6Readiness: nil)
250+
swift6Readiness: nil,
251+
forkedFromURL: nil)
251252
}
252253
}
253254

Sources/App/Models/Repository.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,13 @@ final class Repository: @unchecked Sendable, Model, Content {
224224
.filter(\.$package.$id == pkgId)
225225
.first() ?? Repository(packageId: pkgId)
226226
}
227+
228+
static func find(on database: Database, for packageId: Package.Id) async throws -> Repository? {
229+
return try await Repository.query(on: database)
230+
.filter(\.$package.$id == packageId)
231+
.first()
232+
}
233+
227234
}
228235

229236

Sources/App/Views/PackageController/GetRoute.Model+ext.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,25 @@ extension API.PackageController.GetRoute.Model {
181181
return .empty
182182
}
183183
}
184+
185+
func forkedListItem() -> Node<HTML.ListContext> {
186+
if let forkedFromURL {
187+
let repoURLNode: Node<HTML.BodyContext> = .a(
188+
.href(forkedFromURL),
189+
.text("repository")
190+
)
191+
return .li(
192+
.class("forked"),
193+
.group(
194+
.text("Forked from "),
195+
repoURLNode,
196+
.text(".")
197+
)
198+
)
199+
} else {
200+
return .empty
201+
}
202+
}
184203

185204
func binaryTargetsItem() -> Node<HTML.ListContext> {
186205
guard hasBinaryTargets else { return .empty }

Sources/App/Views/PackageController/PackageShow+View.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ extension PackageShow {
167167
.ul(
168168
.class("main-metadata"),
169169
model.archivedListItem(),
170+
model.forkedListItem(),
170171
model.authorsListItem(),
171172
model.binaryTargetsItem(),
172173
model.historyListItem(),

0 commit comments

Comments
 (0)