Skip to content

Commit 4ee9769

Browse files
Improve ForkedFrom to reflect more info in UI
1 parent ef1d1f6 commit 4ee9769

File tree

7 files changed

+144
-26
lines changed

7 files changed

+144
-26
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
import Fluent
16+
import SQLKit
17+
import Vapor
18+
19+
extension API.PackageController {
20+
enum ForkedFromResult {
21+
case fromSPI(repository: String, owner: String, ownerName: String, packageName: String)
22+
case fromGitHub(url: String)
23+
24+
static func query(on database: Database, packageId: Package.Id) async throws -> ForkedFromResult? {
25+
let model = try await Joined3<Package, Repository, Version>.query(on: database, packageId: packageId).first()
26+
27+
guard let repoName = model?.repository.name,
28+
let ownerName = model?.repository.ownerName,
29+
let owner = model?.repository.owner,
30+
let packageName = model?.version.packageName else {
31+
return nil
32+
}
33+
34+
return ForkedFromResult.fromSPI(
35+
repository: repoName,
36+
owner: owner,
37+
ownerName: ownerName,
38+
packageName: packageName
39+
)
40+
}
41+
}
42+
}

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

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Vapor
2020
extension API.PackageController.GetRoute {
2121
struct Model: Content, Equatable {
2222
var packageId: Package.Id
23+
var packageName: String
2324
var repositoryOwner: String
2425
var repositoryOwnerName: String
2526
var repositoryName: String
@@ -50,9 +51,10 @@ extension API.PackageController.GetRoute {
5051
var releaseReferences: [App.Version.Kind: App.Reference]
5152
var fundingLinks: [FundingLink]
5253
var swift6Readiness: Swift6Readiness?
53-
var forkedFromURL: String?
54+
var forkedFromInfo: ForkedFromInfo?
5455

5556
internal init(packageId: Package.Id,
57+
packageName: String,
5658
repositoryOwner: String,
5759
repositoryOwnerName: String,
5860
repositoryName: String,
@@ -83,9 +85,10 @@ extension API.PackageController.GetRoute {
8385
preReleaseReference: App.Reference?,
8486
fundingLinks: [FundingLink] = [],
8587
swift6Readiness: Swift6Readiness?,
86-
forkedFromURL: String?
88+
forkedFromResult: API.PackageController.ForkedFromResult?
8789
) {
8890
self.packageId = packageId
91+
self.packageName = packageName
8992
self.repositoryOwner = repositoryOwner
9093
self.repositoryOwnerName = repositoryOwnerName
9194
self.repositoryName = repositoryName
@@ -125,7 +128,22 @@ extension API.PackageController.GetRoute {
125128
}()
126129
self.fundingLinks = fundingLinks
127130
self.swift6Readiness = swift6Readiness
128-
self.forkedFromURL = forkedFromURL
131+
if let forkedFromResult {
132+
switch forkedFromResult {
133+
case .fromSPI(let repo, let owner, let ownerName, let packageName):
134+
self.forkedFromInfo = ForkedFromInfo.fromSPI(
135+
packageName: packageName,
136+
originalOwner: owner,
137+
originalOwnerName: ownerName,
138+
originalRepo: repo,
139+
originalPackageName: packageName
140+
)
141+
case .fromGitHub(let url):
142+
self.forkedFromInfo = ForkedFromInfo.fromGitHub(url: url)
143+
}
144+
} else {
145+
self.forkedFromInfo = nil
146+
}
129147
}
130148

131149
init?(result: API.PackageController.PackageResult,
@@ -136,18 +154,20 @@ extension API.PackageController.GetRoute {
136154
platformBuildInfo: BuildInfo<CompatibilityMatrix.PlatformCompatibility>?,
137155
weightedKeywords: [WeightedKeyword] = [],
138156
swift6Readiness: Swift6Readiness?,
139-
forkedFromURL: String?) {
157+
forkedFromResult: API.PackageController.ForkedFromResult?) {
140158
// we consider certain attributes as essential and return nil (raising .notFound)
141159
let repository = result.repository
142160
guard
143161
let repositoryOwner = repository.owner,
144162
let repositoryOwnerName = repository.ownerDisplayName,
145163
let repositoryName = repository.name,
146-
let packageId = result.package.id
164+
let packageId = result.package.id,
165+
let packageName = result.defaultBranchVersion.packageName
147166
else { return nil }
148167

149168
self.init(
150169
packageId: packageId,
170+
packageName: packageName,
151171
repositoryOwner: repositoryOwner,
152172
repositoryOwnerName: repositoryOwnerName,
153173
repositoryName: repositoryName,
@@ -182,7 +202,7 @@ extension API.PackageController.GetRoute {
182202
preReleaseReference: result.preReleaseVersion?.reference,
183203
fundingLinks: result.repository.fundingLinks,
184204
swift6Readiness: swift6Readiness,
185-
forkedFromURL: forkedFromURL
205+
forkedFromResult: forkedFromResult
186206
)
187207

188208
}
@@ -353,6 +373,26 @@ extension API.PackageController.GetRoute.Model {
353373
}
354374
}
355375
}
376+
377+
enum ForkedFromInfo: Codable, Equatable {
378+
case fromSPI(
379+
packageName: String,
380+
originalOwner: String,
381+
originalOwnerName: String,
382+
originalRepo: String,
383+
originalPackageName: String
384+
)
385+
case fromGitHub(url: String)
386+
387+
var url: String {
388+
switch self {
389+
case .fromSPI(_, let originalOwner, _, let originalRepo, _):
390+
return SiteURL.package(.value(originalOwner), .value(originalRepo), nil).absoluteURL()
391+
case .fromGitHub(let url):
392+
return url
393+
}
394+
}
395+
}
356396

357397
}
358398

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension API.PackageController {
3131
owner: owner,
3232
repository: repository)
3333

34-
let forkedFromURL = try await self.fetchForkedFromURL(on: database,
34+
let forkedFromResult = try? await self.fetchForkedFromResult(on: database,
3535
repository: packageResult.repository)
3636

3737
async let weightedKeywords = WeightedKeyword.query(
@@ -60,7 +60,7 @@ extension API.PackageController {
6060
platformBuildInfo: buildInfo.platform,
6161
weightedKeywords: weightedKeywords,
6262
swift6Readiness: buildInfo.swift6Readiness,
63-
forkedFromURL: forkedFromURL
63+
forkedFromResult: forkedFromResult
6464
),
6565
let schema = API.PackageSchema(result: packageResult)
6666
else {
@@ -70,15 +70,14 @@ extension API.PackageController {
7070
return (model, schema)
7171
}
7272

73-
private static func fetchForkedFromURL(on database: Database, repository: Repository) async throws -> String? {
73+
private static func fetchForkedFromResult(on database: Database, repository: Repository) async throws -> ForkedFromResult? {
7474
if let forkedFrom = repository.forkedFrom {
7575
switch forkedFrom {
7676
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
77+
let info = try await ForkedFromResult.query(on: database, packageId: id)
78+
return info
79+
case .parentURL(let url):
80+
return .fromGitHub(url: url)
8281
}
8382
}
8483
return nil

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ extension API.PackageController.GetRoute.Model.History: WithExample {
176176
extension API.PackageController.GetRoute.Model: WithExample {
177177
static var example: Self {
178178
.init(packageId: .example,
179+
packageName: "Mona",
179180
repositoryOwner: "mona",
180181
repositoryOwnerName: "Mona",
181182
repositoryName: "LinkedList",
@@ -248,7 +249,7 @@ extension API.PackageController.GetRoute.Model: WithExample {
248249
releaseReference: .tag(1, 2, 3, "1.2.3"),
249250
preReleaseReference: nil,
250251
swift6Readiness: nil,
251-
forkedFromURL: nil)
252+
forkedFromResult: nil)
252253
}
253254
}
254255

Sources/App/Core/Query+Support/Joined3+Package.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,12 @@ extension Joined3 where M == Package, R1 == Repository, R2 == Version {
4040
.filter(Repository.self, \.$owner, .custom("ilike"), owner)
4141
.filter(Repository.self, \.$name, .custom("ilike"), repository)
4242
}
43+
44+
static func query(on database: Database, packageId: Package.Id) -> JoinedQueryBuilder<Self> {
45+
query(on: database,
46+
join: \Package.$id == \Repository.$package.$id, method: .inner,
47+
join: \Version.$package.$id == \Package.$id, method: .inner)
48+
.filter(Version.self, \Version.$latest == .defaultBranch)
49+
.filter(Package.self, \Package.$id == packageId)
50+
}
4351
}

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

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,45 @@ extension API.PackageController.GetRoute.Model {
183183
}
184184

185185
func forkedListItem() -> Node<HTML.ListContext> {
186-
if let forkedFromURL {
187-
let repoURLNode: Node<HTML.BodyContext> = .a(
188-
.href(forkedFromURL),
189-
.text("repository")
190-
)
186+
if let forkedFromInfo {
187+
let item: Node<HTML.BodyContext> = {
188+
switch forkedFromInfo {
189+
case .fromGitHub(let url):
190+
let repoURLNode: Node<HTML.BodyContext> = .a(
191+
.href(url),
192+
.text("repository")
193+
)
194+
return .group(
195+
.text("Forked from "),
196+
repoURLNode,
197+
.text(".")
198+
)
199+
case .fromSPI(let packageName, let owner, let ownerName, let repo, let originalPackageName):
200+
let repoURLNode: Node<HTML.BodyContext>
201+
if packageName == originalPackageName {
202+
repoURLNode = .a(
203+
.href(forkedFromInfo.url),
204+
.text(ownerName)
205+
)
206+
207+
} else {
208+
repoURLNode = .a(
209+
.href(forkedFromInfo.url),
210+
.text("\(originalPackageName) by \(ownerName)")
211+
)
212+
}
213+
214+
return .group(
215+
.text("Forked from "),
216+
repoURLNode,
217+
.text(".")
218+
)
219+
}
220+
}()
221+
191222
return .li(
192223
.class("forked"),
193-
.group(
194-
.text("Forked from "),
195-
repoURLNode,
196-
.text(".")
197-
)
224+
item
198225
)
199226
} else {
200227
return .empty

Tests/AppTests/Mocks/API.PackageController.GetRoute.Model+mock.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extension API.PackageController.GetRoute.Model {
2121
static var mock: Self {
2222
.init(
2323
packageId: UUID("cafecafe-cafe-cafe-cafe-cafecafecafe")!,
24+
packageName: "Alamofire",
2425
repositoryOwner: "Alamo",
2526
repositoryOwnerName: "Alamofire",
2627
repositoryName: "Alamofire",
@@ -126,7 +127,7 @@ extension API.PackageController.GetRoute.Model {
126127
releaseReference: .tag(5, 2, 0),
127128
preReleaseReference: .tag(5, 3, 0, "beta.1"),
128129
swift6Readiness: nil,
129-
forkedFromURL: nil
130+
forkedFromResult: nil
130131
)
131132
}
132133
}

0 commit comments

Comments
 (0)