diff --git a/Sources/App/Controllers/API/API+PackageController+ForkedFromResult.swift b/Sources/App/Controllers/API/API+PackageController+ForkedFromResult.swift deleted file mode 100644 index 92a0cfeb0..000000000 --- a/Sources/App/Controllers/API/API+PackageController+ForkedFromResult.swift +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Dave Verwer, Sven A. Schmidt, and other contributors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Fluent -import SQLKit -import Vapor - -extension API.PackageController { - enum ForkedFromResult { - case fromSPI(repository: String, owner: String, ownerName: String, packageName: String) - case fromGitHub(url: String) - - static func query(on database: Database, packageId: Package.Id) async throws -> ForkedFromResult? { - let model = try await Joined3 - .query(on: database, packageId: packageId, version: .defaultBranch) - .first() - - guard let repoName = model?.repository.name, - let ownerName = model?.repository.ownerName, - let owner = model?.repository.owner else { - return nil - } - - // fallback to repo name if packageName is nil - let packageName: String = model?.version.packageName ?? repoName - - return ForkedFromResult.fromSPI( - repository: repoName, - owner: owner, - ownerName: ownerName, - packageName: packageName - ) - } - } -} diff --git a/Sources/App/Controllers/API/API+PackageController+GetRoute+Model.swift b/Sources/App/Controllers/API/API+PackageController+GetRoute+Model.swift index 4639d1805..f24c59228 100644 --- a/Sources/App/Controllers/API/API+PackageController+GetRoute+Model.swift +++ b/Sources/App/Controllers/API/API+PackageController+GetRoute+Model.swift @@ -83,7 +83,7 @@ extension API.PackageController.GetRoute { preReleaseReference: App.Reference?, fundingLinks: [FundingLink] = [], swift6Readiness: Swift6Readiness?, - forkedFromResult: API.PackageController.ForkedFromResult? + forkedFromInfo: ForkedFromInfo? ) { self.packageId = packageId self.repositoryOwner = repositoryOwner @@ -125,22 +125,7 @@ extension API.PackageController.GetRoute { }() self.fundingLinks = fundingLinks self.swift6Readiness = swift6Readiness - if let forkedFromResult { - switch forkedFromResult { - case .fromSPI(let repo, let owner, let ownerName, let packageName): - self.forkedFromInfo = ForkedFromInfo.fromSPI( - packageName: self.title, - originalOwner: owner, - originalOwnerName: ownerName, - originalRepo: repo, - originalPackageName: packageName - ) - case .fromGitHub(let url): - self.forkedFromInfo = ForkedFromInfo.fromGitHub(url: url) - } - } else { - self.forkedFromInfo = nil - } + self.forkedFromInfo = forkedFromInfo } init?(result: API.PackageController.PackageResult, @@ -151,7 +136,7 @@ extension API.PackageController.GetRoute { platformBuildInfo: BuildInfo?, weightedKeywords: [WeightedKeyword] = [], swift6Readiness: Swift6Readiness?, - forkedFromResult: API.PackageController.ForkedFromResult?) { + forkedFromInfo: ForkedFromInfo?) { // we consider certain attributes as essential and return nil (raising .notFound) let repository = result.repository guard @@ -197,7 +182,7 @@ extension API.PackageController.GetRoute { preReleaseReference: result.preReleaseVersion?.reference, fundingLinks: result.repository.fundingLinks, swift6Readiness: swift6Readiness, - forkedFromResult: forkedFromResult + forkedFromInfo: forkedFromInfo ) } @@ -370,32 +355,11 @@ extension API.PackageController.GetRoute.Model { } enum ForkedFromInfo: Codable, Equatable { - case fromSPI( - packageName: String, - originalOwner: String, - originalOwnerName: String, - originalRepo: String, - originalPackageName: String - ) + case fromSPI(originalOwner: String, + originalOwnerName: String, + originalRepo: String, + originalPackageName: String) case fromGitHub(url: String) - - var url: String { - switch self { - case .fromSPI(_, let originalOwner, _, let originalRepo, _): - return SiteURL.package(.value(originalOwner), .value(originalRepo), nil).relativeURL() - case .fromGitHub(let url): - return url - } - } - - var ownerURL: String? { - switch self { - case .fromSPI(_, let owner, _, _, _): - return SiteURL.author(.value(owner)).relativeURL() - case .fromGitHub: - return nil - } - } } } diff --git a/Sources/App/Controllers/API/API+PackageController+GetRoute.swift b/Sources/App/Controllers/API/API+PackageController+GetRoute.swift index 546f2f2df..2e52654ce 100644 --- a/Sources/App/Controllers/API/API+PackageController+GetRoute.swift +++ b/Sources/App/Controllers/API/API+PackageController+GetRoute.swift @@ -30,10 +30,6 @@ extension API.PackageController { let packageResult = try await PackageResult.query(on: database, owner: owner, repository: repository) - - let forkedFromResult = try? await self.fetchForkedFromResult(on: database, - repository: packageResult.repository) - async let weightedKeywords = WeightedKeyword.query( on: database, keywords: packageResult.repository.keywords ) @@ -49,6 +45,7 @@ extension API.PackageController { async let buildInfo = API.PackageController.BuildInfo.query(on: database, owner: owner, repository: repository) + async let forkedFromInfo = forkedFromInfo(on: database, fork: packageResult.repository.forkedFrom) guard let model = try await Self.Model( @@ -60,7 +57,7 @@ extension API.PackageController { platformBuildInfo: buildInfo.platform, weightedKeywords: weightedKeywords, swift6Readiness: buildInfo.swift6Readiness, - forkedFromResult: forkedFromResult + forkedFromInfo: forkedFromInfo ), let schema = API.PackageSchema(result: packageResult) else { @@ -69,19 +66,6 @@ extension API.PackageController { return (model, schema) } - - static func fetchForkedFromResult(on database: Database, repository: Repository) async throws -> ForkedFromResult? { - if let forkedFrom = repository.forkedFrom { - switch forkedFrom { - case .parentId(let id): - let info = try await ForkedFromResult.query(on: database, packageId: id) - return info - case .parentURL(let url): - return .fromGitHub(url: url) - } - } - return nil - } } } @@ -102,4 +86,34 @@ extension API.PackageController.GetRoute { beta: links[1], latest: links[2]) } + + static func forkedFromInfo(on database: Database, fork: Fork?) async -> Model.ForkedFromInfo? { + guard let forkedFrom = fork else { return nil } + switch forkedFrom { + case let .parentId(id): + return await Model.ForkedFromInfo.query(on: database, packageId: id) + case let .parentURL(url): + return .fromGitHub(url: url) + } + } +} + + +extension API.PackageController.GetRoute.Model.ForkedFromInfo { + static func query(on database: Database, packageId: Package.Id) async -> Self? { + let model = try? await Joined3 + .query(on: database, packageId: packageId, version: .defaultBranch) + .first() + + guard let repoName = model?.repository.name, + let ownerName = model?.repository.ownerName, + let owner = model?.repository.owner else { + return nil + } + + return .fromSPI(originalOwner: owner, + originalOwnerName: ownerName, + originalRepo: repoName, + originalPackageName: model?.version.packageName ?? repoName) + } } diff --git a/Sources/App/Controllers/API/Types+WithExample.swift b/Sources/App/Controllers/API/Types+WithExample.swift index 900dacfab..a15170f81 100644 --- a/Sources/App/Controllers/API/Types+WithExample.swift +++ b/Sources/App/Controllers/API/Types+WithExample.swift @@ -248,7 +248,7 @@ extension API.PackageController.GetRoute.Model: WithExample { releaseReference: .tag(1, 2, 3, "1.2.3"), preReleaseReference: nil, swift6Readiness: nil, - forkedFromResult: nil) + forkedFromInfo: nil) } } diff --git a/Sources/App/Views/PackageController/GetRoute.Model+ext.swift b/Sources/App/Views/PackageController/GetRoute.Model+ext.swift index 5dd2d9f14..b647d1696 100644 --- a/Sources/App/Views/PackageController/GetRoute.Model+ext.swift +++ b/Sources/App/Views/PackageController/GetRoute.Model+ext.swift @@ -198,7 +198,7 @@ extension API.PackageController.GetRoute.Model { repoURLNode, .text(".") ) - case .fromSPI(_, _, let ownerName, _, let originalPackageName): + case .fromSPI(_, let ownerName, _, let originalPackageName): let repoURLNode: Node = .a( .href(forkedFromInfo.url), .text("\(originalPackageName)") @@ -711,3 +711,24 @@ extension API.PackageController.GetRoute.Model.Swift6Readiness { return lines.joined(separator: "\n") } } + + +extension API.PackageController.GetRoute.Model.ForkedFromInfo { + var url: String { + switch self { + case .fromSPI(let originalOwner, _, let originalRepo, _): + return SiteURL.package(.value(originalOwner), .value(originalRepo), nil).relativeURL() + case .fromGitHub(let url): + return url + } + } + + var ownerURL: String? { + switch self { + case .fromSPI(let owner, _, _, _): + return SiteURL.author(.value(owner)).relativeURL() + case .fromGitHub: + return nil + } + } +} diff --git a/Tests/AppTests/API+PackageController+GetRoute+ModelTests.swift b/Tests/AppTests/API+PackageController+GetRoute+ModelTests.swift index 734d253f9..5f9af09a6 100644 --- a/Tests/AppTests/API+PackageController+GetRoute+ModelTests.swift +++ b/Tests/AppTests/API+PackageController+GetRoute+ModelTests.swift @@ -21,7 +21,6 @@ import SPIManifest class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { typealias PackageResult = PackageController.PackageResult - typealias ForkedFromResult = API.PackageController.ForkedFromResult func test_init_no_packageName() async throws { // Tests behaviour when we're lacking data @@ -44,7 +43,7 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { platformBuildInfo: nil, weightedKeywords: [], swift6Readiness: nil, - forkedFromResult: nil) + forkedFromInfo: nil) // validate XCTAssertNotNil(m) @@ -67,8 +66,7 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { platformBuildInfo: nil, weightedKeywords: [], swift6Readiness: nil, - forkedFromResult: nil - )) + forkedFromInfo: nil)) // validate XCTAssertEqual(model.packageIdentity, "swift-bar") @@ -91,8 +89,7 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { platformBuildInfo: nil, weightedKeywords: [], swift6Readiness: nil, - forkedFromResult: nil - )) + forkedFromInfo: nil)) // validate XCTAssertEqual(model.documentationTarget, .internal(docVersion: .reference("main"), archive: "archive1")) @@ -119,84 +116,29 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { platformBuildInfo: nil, weightedKeywords: [], swift6Readiness: nil, - forkedFromResult: nil - )) + forkedFromInfo: nil)) // validate XCTAssertEqual(model.documentationTarget, .external(url: "https://example.com/package/documentation")) } - func test_init_forked_from_github() async throws { - let pkg = try await savePackage(on: app.db, "1".url) - let repo = try Repository(package: pkg, forkedFrom: .parentURL("https://github.com/example/repo.git"), name: "bar", owner: "foo") - try await repo.save(on: app.db) - let version = try App.Version(package: pkg, latest: .defaultBranch, packageName: nil, reference: .branch("main")) - try await version.save(on: app.db) - let packageResult = try await PackageResult.query(on: app.db, owner: "foo", repository: "bar") - let forkedFromResult = try await API.PackageController.GetRoute.fetchForkedFromResult(on: app.db, repository: packageResult.repository) - - // MUT - let model = try XCTUnwrap(API.PackageController.GetRoute.Model(result: packageResult, - history: nil, - products: [], - targets: [], - swiftVersionBuildInfo: nil, - platformBuildInfo: nil, - weightedKeywords: [], - swift6Readiness: nil, - forkedFromResult: forkedFromResult - )) - - // validate - XCTAssertEqual(model.forkedFromInfo, API.PackageController.GetRoute.Model.ForkedFromInfo.fromGitHub(url: "https://github.com/example/repo.git")) - } - - func test_init_forked_from_spi() async throws { - let originalPkg = try await savePackage(on: app.db, "https://github.com/original/original") - let originalRepo = try Repository( - package: originalPkg, - name: "original", - owner: "original", - ownerName: "OriginalOwner" - ) - try await originalRepo.save(on: app.db) - let originalVersion = try App.Version(package: originalPkg, latest: .defaultBranch, packageName: "OriginalPkg", reference: .branch("main")) - try await originalVersion.save(on: app.db) - let pkg = try await savePackage(on: app.db, "1".url) - let repo = try Repository( - package: pkg, - forkedFrom: .parentId(originalPkg.requireID()), - name: "bar", - owner: "foo" - ) - try await repo.save(on: app.db) - let version = try App.Version(package: pkg, latest: .defaultBranch, packageName: nil, reference: .branch("main")) - try await version.save(on: app.db) - - let packageResult = try await PackageResult.query(on: app.db, owner: "foo", repository: "bar") - let forkedFromResult = try await API.PackageController.GetRoute.fetchForkedFromResult(on: app.db, repository: packageResult.repository) + func test_ForkedFromInfo_query() async throws { + let originalPkg = try await savePackage(on: app.db, id: .id0, "https://github.com/original/original") + try await Repository(package: originalPkg, + name: "original", + owner: "original", + ownerName: "OriginalOwner").save(on: app.db) + try await App.Version(package: originalPkg, latest: .defaultBranch, packageName: "OriginalPkg", reference: .branch("main")) + .save(on: app.db) // MUT - let model = try XCTUnwrap(API.PackageController.GetRoute.Model(result: packageResult, - history: nil, - products: [], - targets: [], - swiftVersionBuildInfo: nil, - platformBuildInfo: nil, - weightedKeywords: [], - swift6Readiness: nil, - forkedFromResult: forkedFromResult - )) + let forkedFrom = await API.PackageController.GetRoute.Model.ForkedFromInfo.query(on: app.db, packageId: .id0) // validate - let info = API.PackageController.GetRoute.Model.ForkedFromInfo.fromSPI( - packageName: "bar", - originalOwner: "original", - originalOwnerName: "OriginalOwner", - originalRepo: "original", - originalPackageName: "OriginalPkg" - ) - XCTAssertEqual(model.forkedFromInfo, info) + XCTAssertEqual(forkedFrom, .fromSPI(originalOwner: "original", + originalOwnerName: "OriginalOwner", + originalRepo: "original", + originalPackageName: "OriginalPkg")) } func test_gitHubOwnerUrl() throws { @@ -236,7 +178,6 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { func test_forked_from_spi_same_package_name() throws { var model = API.PackageController.GetRoute.Model.mock model.forkedFromInfo = .fromSPI( - packageName: "Test", originalOwner: "owner", originalOwnerName: "OriginalOwner", originalRepo: "repo", @@ -249,7 +190,6 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { func test_forked_from_spi_different_package_name() throws { var model = API.PackageController.GetRoute.Model.mock model.forkedFromInfo = .fromSPI( - packageName: "Test", originalOwner: "owner", originalOwnerName: "OriginalOwner", originalRepo: "repo", @@ -454,7 +394,6 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { func test_forkedFrom_spi_same_package_name_formatting() throws { var model = API.PackageController.GetRoute.Model.mock model.forkedFromInfo = .fromSPI( - packageName: "Test", originalOwner: "owner", originalOwnerName: "OriginalOwner", originalRepo: "repo", @@ -469,7 +408,6 @@ class API_PackageController_GetRoute_ModelTests: SnapshotTestCase { func test_forkedFrom_spi_different_package_name_formatting() throws { var model = API.PackageController.GetRoute.Model.mock model.forkedFromInfo = .fromSPI( - packageName: "Test", originalOwner: "owner", originalOwnerName: "OriginalOwner", originalRepo: "repo", diff --git a/Tests/AppTests/Mocks/API.PackageController.GetRoute.Model+mock.swift b/Tests/AppTests/Mocks/API.PackageController.GetRoute.Model+mock.swift index 2a9fff3bf..31da3f38e 100644 --- a/Tests/AppTests/Mocks/API.PackageController.GetRoute.Model+mock.swift +++ b/Tests/AppTests/Mocks/API.PackageController.GetRoute.Model+mock.swift @@ -126,7 +126,7 @@ extension API.PackageController.GetRoute.Model { releaseReference: .tag(5, 2, 0), preReleaseReference: .tag(5, 3, 0, "beta.1"), swift6Readiness: nil, - forkedFromResult: nil + forkedFromInfo: nil ) } }