diff --git a/GraphQLNetworkTester.xcodeproj/project.pbxproj b/GraphQLNetworkTester.xcodeproj/project.pbxproj index 47fb183..b7b769a 100644 --- a/GraphQLNetworkTester.xcodeproj/project.pbxproj +++ b/GraphQLNetworkTester.xcodeproj/project.pbxproj @@ -17,6 +17,9 @@ AC162E262A923B0200990143 /* GeneratedModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC162E252A923B0200990143 /* GeneratedModels.swift */; }; AC162E2A2A92578800990143 /* DataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC162E292A92578800990143 /* DataLoader.swift */; }; AC162E2C2A9257B200990143 /* Queries.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC162E2B2A9257B200990143 /* Queries.swift */; }; + ACE87A7E2AB1462100EF4ACD /* FetchPRDataCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE87A7D2AB1462100EF4ACD /* FetchPRDataCoordinator.swift */; }; + ACE87A822AB1471300EF4ACD /* RepoFetchObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE87A812AB1471300EF4ACD /* RepoFetchObject.swift */; }; + ACE87A852AB14D3E00EF4ACD /* Queue in Frameworks */ = {isa = PBXBuildFile; productRef = ACE87A842AB14D3E00EF4ACD /* Queue */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,6 +54,8 @@ AC162E252A923B0200990143 /* GeneratedModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedModels.swift; sourceTree = ""; }; AC162E292A92578800990143 /* DataLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoader.swift; sourceTree = ""; }; AC162E2B2A9257B200990143 /* Queries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queries.swift; sourceTree = ""; }; + ACE87A7D2AB1462100EF4ACD /* FetchPRDataCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchPRDataCoordinator.swift; sourceTree = ""; }; + ACE87A812AB1471300EF4ACD /* RepoFetchObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepoFetchObject.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,6 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + ACE87A852AB14D3E00EF4ACD /* Queue in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -109,6 +115,8 @@ AC162E252A923B0200990143 /* GeneratedModels.swift */, AC162E292A92578800990143 /* DataLoader.swift */, AC162E2B2A9257B200990143 /* Queries.swift */, + ACE87A7D2AB1462100EF4ACD /* FetchPRDataCoordinator.swift */, + ACE87A812AB1471300EF4ACD /* RepoFetchObject.swift */, ); path = GraphQLNetworkTester; sourceTree = ""; @@ -154,6 +162,9 @@ dependencies = ( ); name = GraphQLNetworkTester; + packageProductDependencies = ( + ACE87A842AB14D3E00EF4ACD /* Queue */, + ); productName = GraphQLNetworkTester; productReference = AC162DF72A923AE400990143 /* GraphQLNetworkTester.app */; productType = "com.apple.product-type.application"; @@ -226,6 +237,9 @@ Base, ); mainGroup = AC162DEE2A923AE400990143; + packageReferences = ( + ACE87A832AB14D3E00EF4ACD /* XCRemoteSwiftPackageReference "Queue" */, + ); productRefGroup = AC162DF82A923AE400990143 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -269,10 +283,12 @@ buildActionMask = 2147483647; files = ( AC162E2C2A9257B200990143 /* Queries.swift in Sources */, + ACE87A7E2AB1462100EF4ACD /* FetchPRDataCoordinator.swift in Sources */, AC162E2A2A92578800990143 /* DataLoader.swift in Sources */, AC162DFD2A923AE400990143 /* ContentView.swift in Sources */, AC162DFB2A923AE400990143 /* GraphQLNetworkTesterApp.swift in Sources */, AC162E262A923B0200990143 /* GeneratedModels.swift in Sources */, + ACE87A822AB1471300EF4ACD /* RepoFetchObject.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -588,6 +604,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + ACE87A832AB14D3E00EF4ACD /* XCRemoteSwiftPackageReference "Queue" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/mattmassicotte/Queue"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.4; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + ACE87A842AB14D3E00EF4ACD /* Queue */ = { + isa = XCSwiftPackageProductDependency; + package = ACE87A832AB14D3E00EF4ACD /* XCRemoteSwiftPackageReference "Queue" */; + productName = Queue; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = AC162DEF2A923AE400990143 /* Project object */; } diff --git a/GraphQLNetworkTester.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/GraphQLNetworkTester.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..42eae05 --- /dev/null +++ b/GraphQLNetworkTester.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "queue", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattmassicotte/Queue", + "state" : { + "revision" : "8d6f936097888f97011610ced40313655dc5948d", + "version" : "0.1.4" + } + } + ], + "version" : 2 +} diff --git a/GraphQLNetworkTester/ContentView.swift b/GraphQLNetworkTester/ContentView.swift index bf6539c..35923b6 100644 --- a/GraphQLNetworkTester/ContentView.swift +++ b/GraphQLNetworkTester/ContentView.swift @@ -17,6 +17,12 @@ struct ContentView: View { var body: some View { HStack { VStack { + Button("Get PRs") { + Task { + await FetchPRDataCoordinator().getPRs() + } + } + Text("Fetch Data for Pull Request") .padding(1) Form { @@ -26,19 +32,21 @@ struct ContentView: View { Button("Fetch PR") { Task { print("Attempting to fetch data for owner: \(owner), repo: \(repo), number: \(number)") - let result = try await DataLoader().request(forQraphQLQuery: .getPullRequestData(forOwner: owner, inRepo: repo, withNumber: number)) +// let result = try await DataLoader().request(forQraphQLQuery: .getPullRequestData(forOwner: owner, inRepo: repo, withNumber: number)) + let result = await fetchPRDataFromGitHub(forOwner: owner, inRepo: repo, forNumber: number) if let validData = result { - print("We're in the unwrapped return value and have some data!") - let stringData = String(data: validData, encoding: String.Encoding.ascii) - print("String data is: \(String(describing: stringData))") - - let decodedPRData = try? JSONDecoder().decode(DecodePRStartingPoint.self, from: validData) - if let unwrappedDecodedData = decodedPRData { - print("Successfully decoded data! The PR number is: ") - print(unwrappedDecodedData.data.repository.pullRequest.number) - } else { - print("No data here! Couldn't decode it.") - } + print("I've got \(validData.commits.nodes.count) commits") +// print("We're in the unwrapped return value and have some data!") +// let stringData = String(data: validData, encoding: String.Encoding.ascii) +// print("String data is: \(String(describing: stringData))") +// +// let decodedPRData = try? JSONDecoder().decode(DecodePRStartingPoint.self, from: validData) +// if let unwrappedDecodedData = validData { +// print("Successfully decoded data! The PR number is: ") +// print(unwrappedDecodedData.data.repository.pullRequest.number) +// } else { +// print("No data here! Couldn't decode it.") +// } } } } diff --git a/GraphQLNetworkTester/DataLoader.swift b/GraphQLNetworkTester/DataLoader.swift index 18ea519..637847b 100644 --- a/GraphQLNetworkTester/DataLoader.swift +++ b/GraphQLNetworkTester/DataLoader.swift @@ -8,8 +8,10 @@ import Foundation class DataLoader { + var remainingAPIRequests = 0 + func request(forQraphQLQuery: GraphQLQuery) async throws -> Data? { - let token = "" + let token = "INSERT-YOUR-API-KEY-HERE" let gitHubGraphQLURL = URL(string: "https://api.github.com/graphql") @@ -46,12 +48,30 @@ class DataLoader { // Code might suspend here let (data, response) = try await URLSession.shared.data(for: apiRequest) - guard (response as? HTTPURLResponse)?.statusCode == 200 else { - throw DataFetcherError.serverError + let httpUrlResponse = response as? HTTPURLResponse + if let unwrappedResponse = httpUrlResponse { + let statusCode = unwrappedResponse.statusCode + + if statusCode == 200 { + let remainingRequests = unwrappedResponse.value(forHTTPHeaderField: "x-ratelimit-remaining") + if let unwrappedOptionalRemainingRequests = remainingRequests { + let remainingRequestsAsInt = Int(unwrappedOptionalRemainingRequests) + if let validInt = remainingRequestsAsInt { + remainingAPIRequests = validInt + print("Available requests remaining: \(remainingAPIRequests)") + } + } + return data + } else { + print("There was an error fetching data from GitHub: \(statusCode.description)") + let decodedErrorInfo = try? JSONDecoder().decode(DecodeGitHubAPIErrorBody.self, from: data) + if let unwrappedErrorMessage = decodedErrorInfo { + print("The GitHub error message was: \(unwrappedErrorMessage)") + throw DataFetcherError.serverError(unwrappedErrorMessage.message) + } + throw DataFetcherError.serverError("There was no decoded error message from GitHub") + } } - - //print(response as? HTTPURLResponse) - return data } } @@ -60,5 +80,5 @@ enum DataFetcherError: Error { case invalidURL case notFound case typeNotFound - case serverError + case serverError(String?) } diff --git a/GraphQLNetworkTester/FetchPRDataCoordinator.swift b/GraphQLNetworkTester/FetchPRDataCoordinator.swift new file mode 100644 index 0000000..9e6b18d --- /dev/null +++ b/GraphQLNetworkTester/FetchPRDataCoordinator.swift @@ -0,0 +1,256 @@ +// +// FetchPRDataCoordinator.swift +// GraphQLNetworkTester +// +// Created by Dachary Carey on 9/12/23. +// + +import Foundation +import Queue + +class FetchPRDataCoordinator { + let queue = AsyncQueue() + var repoFetchResultsDocsRealm = RepoFetchResult(repoFetchObject: RepoFetchObject.docsRealm) + var repoFetchResultsDocsAppx = RepoFetchResult(repoFetchObject: RepoFetchObject.docsAppServices) + var repoFetchResultsRealmSwift = RepoFetchResult(repoFetchObject: RepoFetchObject.realmSwift) + var repoFetchResultsRealmKotlin = RepoFetchResult(repoFetchObject: RepoFetchObject.realmKotlin) + var repoFetchResultsRealmJS = RepoFetchResult(repoFetchObject: RepoFetchObject.realmJS) + var repoFetchResultsRealmCpp = RepoFetchResult(repoFetchObject: RepoFetchObject.realmCpp) + + var reposToFetch: [RepoFetchResult] = [] + + init() { + self.reposToFetch = [repoFetchResultsRealmSwift, repoFetchResultsDocsAppx, repoFetchResultsDocsRealm, repoFetchResultsRealmJS, repoFetchResultsRealmCpp, repoFetchResultsRealmKotlin] + } + + func addPRsToPRToFetchList(for repo: RepoFetchResult, _ openPRList: [Int]) -> RepoFetchResult { + for openPRNumber in openPRList { + if !repo.repoFetchObject.pullRequestNumbersToFetch.contains(openPRNumber) { + repo.repoFetchObject.pullRequestNumbersToFetch.append(openPRNumber) + print("Added PR # \(openPRNumber) to list of PRs to fetch for repo \(repo.repoFetchObject.repoOwner)/\(repo.repoFetchObject.repoName)") + } + } + return repo + } + + func getPRs() async { + var updatedRepo: RepoFetchResult? = nil + + for repo in reposToFetch { + let openPRs = await fetchOpenPRsFromGitHub(forOwner: repo.repoFetchObject.repoOwner, inRepo: repo.repoFetchObject.repoName) + if let openPRs { + updatedRepo = self.addPRsToPRToFetchList(for: repo, openPRs) + } else { + print("Did not get a valid list of open PRs from GitHub for repo \(repo.repoFetchObject.repoOwner)/\(repo.repoFetchObject.repoName)") + } + if let updatedRepo { + for prNumber in updatedRepo.repoFetchObject.pullRequestNumbersToFetch { + queue.addOperation { + let tempPR = await fetchPRDataFromGitHub(forOwner: updatedRepo.repoFetchObject.repoOwner, inRepo: updatedRepo.repoFetchObject.repoName, forNumber: prNumber) + let tempStatusCheckPR = await fetchPRStatusCheckDataFromGitHub(forOwner: updatedRepo.repoFetchObject.repoOwner, inRepo: updatedRepo.repoFetchObject.repoName, forNumber: prNumber) + if let tempPR { + updatedRepo.prObjects.append(tempPR) + print("Fetched PR \(prNumber) in repo \(updatedRepo.repoFetchObject.repoName)") + } + if let tempStatusCheckPR { + updatedRepo.prStatusChecks.append(tempStatusCheckPR) + } + } + } + reposToFetch.removeAll(where: { + $0.repoFetchObject.repoId == updatedRepo.repoFetchObject.repoId + }) + reposToFetch.append(updatedRepo) + } else { + for prNumber in repo.repoFetchObject.pullRequestNumbersToFetch { + queue.addOperation { + let tempPR = await fetchPRDataFromGitHub(forOwner: repo.repoFetchObject.repoOwner, inRepo: repo.repoFetchObject.repoName, forNumber: prNumber) + let tempStatusCheckPR = await fetchPRStatusCheckDataFromGitHub(forOwner: repo.repoFetchObject.repoOwner, inRepo: repo.repoFetchObject.repoName, forNumber: prNumber) + if let tempPR { + var mutablePR = tempPR + if tempPR.commits.pageInfo.hasPreviousPage { + if let unwrappedStartCursor = tempPR.commits.pageInfo.startCursor { + let additionalCommits = await fetchPaginatedCommitsFromGitHub(forOwner: repo.repoFetchObject.repoOwner, inRepo: repo.repoFetchObject.repoName, forNumber: prNumber, forStartCursor: unwrappedStartCursor) + if let unwrappedCommits = additionalCommits?.commits { + mutablePR.commits.nodes.append(contentsOf: unwrappedCommits) + } + } + } + repo.prObjects.append(mutablePR) + print("Fetched PR \(prNumber) in repo \(repo.repoFetchObject.repoName)") + print("PR commit count is: \(mutablePR.commits.nodes.count)") + } + if let tempStatusCheckPR { + repo.prStatusChecks.append(tempStatusCheckPR) + } + } + } + } + updatedRepo = nil + } + + queue.addBarrierOperation { + let realmSwiftFetchedPRs = self.reposToFetch[0].prObjects.count + let realmSwiftFetchedStatusCheckPRs = self.reposToFetch[0].prStatusChecks.count + print("The count for realm Swift is \(realmSwiftFetchedPRs) PRs fetched and \(realmSwiftFetchedStatusCheckPRs) PR status checks") + + let docsAppxFetchedPRs = self.reposToFetch[1].prObjects.count + let docsAppxFetchedStatusCheckPRs = self.reposToFetch[1].prStatusChecks.count + print("The count for docs appx is \(docsAppxFetchedPRs) PRs fetched and \(docsAppxFetchedStatusCheckPRs) PR status checks") + + let docsRealmFetchedPRs = self.reposToFetch[2].prObjects.count + let docsRealmFetchedStatusCheckPRs = self.reposToFetch[2].prStatusChecks.count + print("The count for docs realm is \(docsRealmFetchedPRs) PRs fetched and \(docsRealmFetchedStatusCheckPRs) PR status checks") + + let realmJSFetchedPRs = self.reposToFetch[3].prObjects.count + let realmJSFetchedStatusCheckPRs = self.reposToFetch[3].prStatusChecks.count + print("The count for realm JS is \(realmJSFetchedPRs) PRs fetched and \(realmJSFetchedStatusCheckPRs) PR status checks") + + let realmCPPFetchedPRs = self.reposToFetch[4].prObjects.count + let realmCPPFetchedStatusCheckPRs = self.reposToFetch[4].prStatusChecks.count + print("The count for realm CPP is \(realmCPPFetchedPRs) PRs fetched and \(realmCPPFetchedStatusCheckPRs) PR status checks") + + let realmKotlinFetchedPRs = self.reposToFetch[5].prObjects.count + let realmKotlinFetchedStatusCheckPRs = self.reposToFetch[5].prStatusChecks.count + print("The count for realm kotlin is \(realmKotlinFetchedPRs) PRs fetched and \(realmKotlinFetchedStatusCheckPRs) PR status checks") + } + } +} + +func fetchOpenPRsFromGitHub(forOwner owner: String, inRepo repo: String) async -> [Int]? { + do { + let data = try await DataLoader().request(forQraphQLQuery: .getPRsInRepository(forOwner: owner, inRepo: repo)) + guard let data else { + print("There was an error fetching data from GitHub") + return nil + } + let decodedPRList = try? JSONDecoder().decode(DecodePRListInRepoStartingPoint.self, from: data) + let decodedPRNumbers = decodedPRList?.data.repository.pullRequests.nodes + if let decodedPRNumbers { + var pullRequestNumbers: [Int] = [] + for decodedPR in decodedPRNumbers { + pullRequestNumbers.append(decodedPR.number) + } + return pullRequestNumbers + } + } catch { + // TODO: Surface this error + print("Error fetching data or something: \(error.localizedDescription)") + } + return nil +} + +func fetchPRDataFromGitHub(forOwner owner: String, inRepo repo: String, forNumber number: Int) async -> PullRequest_Temp? { + var commitsHavePreviousPage = false + var startCursor = "" + do { + let data = try await DataLoader().request(forQraphQLQuery: .getPullRequestData(forOwner: owner, inRepo: repo, withNumber: number)) + guard let data else { + print("There was an error fetching data from GitHub") + return nil + } + let decodedPRData = try? JSONDecoder().decode(DecodePRStartingPoint.self, from: data) + if let unwrappedDecodedPRData = decodedPRData?.data.repository.pullRequest { + var mutablePRData = unwrappedDecodedPRData + let totalCommitCount = mutablePRData.commits.totalCount + // Set the empty string start cursor to the start cursor returned by the query + // Set the commitsHavePreviousPage bool to start a loop to fetch previous commits + if let unwrappedStartCursorFromQuery = unwrappedDecodedPRData.commits.pageInfo.startCursor { + startCursor = unwrappedStartCursorFromQuery + commitsHavePreviousPage = unwrappedDecodedPRData.commits.pageInfo.hasPreviousPage + while commitsHavePreviousPage { + DispatchQueue.main.sync { + print("I have PR data and hasPreviousPage is \(unwrappedDecodedPRData.commits.pageInfo.hasPreviousPage)") + print("Attempting to fetch additional commits for start cursor \(unwrappedDecodedPRData.commits.pageInfo.startCursor)") + } + if startCursor != "" { + let additionalCommitData = await fetchPaginatedCommitsFromGitHub(forOwner: owner, inRepo: repo, forNumber: number, forStartCursor: startCursor) + if let unwrappedCommitData = additionalCommitData { + if let unwrappedAdditionalCommits = unwrappedCommitData.commits { + mutablePRData.commits.nodes.append(contentsOf: unwrappedAdditionalCommits) + DispatchQueue.main.sync { + print("I have additional commits \(unwrappedAdditionalCommits.count)") + } + } + // After fetching the new info, if the commits still have a previous page, + // set these variable values so we can loop through and fetch those, too + commitsHavePreviousPage = unwrappedCommitData.hasPreviousPage + if let unwrappedNewStartCursor = unwrappedCommitData.startCursor { + startCursor = unwrappedNewStartCursor + } + } + } else { + DispatchQueue.main.sync { + print("The commit query has a previous page, but I can't find the start cursor to fetch the next page of commits") + } + break + } + } + } + if totalCommitCount == mutablePRData.commits.nodes.count { + DispatchQueue.main.sync { + print("The total number of commits is \(totalCommitCount) and matches the number of commits after fetching paginated commits.") + } + } else { + DispatchQueue.main.sync { + print("The total commits should be \(totalCommitCount) but the PR only has \(mutablePRData.commits.nodes.count) - something has gone wrong.") + } + } + return mutablePRData + } + } catch { + print("Error fetching data or something: \(error.localizedDescription)") + } + return nil +} + +func fetchPRStatusCheckDataFromGitHub(forOwner owner: String, inRepo repo: String, forNumber number: Int) async -> StatusCheckPullRequest_Temp? { + do { + let data = try await DataLoader().request(forQraphQLQuery: .getPullRequestData(forOwner: owner, inRepo: repo, withNumber: number)) + guard let data else { + print("There was an error fetching data from GitHub") + return nil + } + let decodedPRData = try? JSONDecoder().decode(DecodeStatusCheckStartingPoint.self, from: data) + if let unwrappedDecodedPRStatusCheckData = decodedPRData?.data.repository.pullRequest { + return unwrappedDecodedPRStatusCheckData + } + } catch { + print("Error fetching data or something: \(error.localizedDescription)") + } + return nil +} + +struct PaginatedCommitInfo { + var commits: [CommitNode]? + var hasPreviousPage: Bool + var startCursor: String? +} + +func fetchPaginatedCommitsFromGitHub(forOwner owner: String, inRepo repo: String, forNumber number: Int, forStartCursor startCursor: String) async -> PaginatedCommitInfo? { + do { + let data = try await DataLoader().request(forQraphQLQuery: .getPaginatedCommitData(forOwner: owner, inRepo: repo, withNumber: number, forStartCursor: startCursor)) + guard let data else { + DispatchQueue.main.sync { + print("There was an error fetching paginated commit data from GitHub - no data from the data loader") + } + return nil + } +// DispatchQueue.main.sync { +// let stringData = String(data: data, encoding: String.Encoding.ascii) +// print("The paginated commit endpoint returned \(stringData)") +// } + let decodedCommitData = try? JSONDecoder().decode(DecodePaginatedCommitsStartingPoint.self, from: data) + if let unwrappedDecodedCommitData = decodedCommitData { + var paginatedCommitInfo = PaginatedCommitInfo(commits: unwrappedDecodedCommitData.data.repository.pullRequest.commits.nodes, hasPreviousPage: unwrappedDecodedCommitData.data.repository.pullRequest.commits.pageInfo.hasPreviousPage, startCursor: unwrappedDecodedCommitData.data.repository.pullRequest.commits.pageInfo.startCursor) + return paginatedCommitInfo + } else { + DispatchQueue.main.sync { + print("There was an error decoding commit data") + } + } + } catch { + print("Error fetching paginated commit data: \(error.localizedDescription)") + } + return nil +} diff --git a/GraphQLNetworkTester/GeneratedModels.swift b/GraphQLNetworkTester/GeneratedModels.swift index 688863f..4f2a7d3 100644 --- a/GraphQLNetworkTester/GeneratedModels.swift +++ b/GraphQLNetworkTester/GeneratedModels.swift @@ -12,6 +12,14 @@ import Foundation +struct DecodeGitHubAPIErrorBody: Codable { + let message: String + + enum CodingKeys: String, CodingKey { + case message + } +} + // MARK: DecodePRListInRepoStartingPoint struct DecodePRListInRepoStartingPoint: Codable { let data: InterimRepositoryStruct @@ -102,13 +110,50 @@ struct PullRequest_Temp: Codable { let assignees: AssigneesConnection let comments: IssueCommentConnection let reviews: PullRequestReviewConnection? - let commits: PullRequestCommitConnection + var commits: PullRequestCommitConnection enum CodingKeys: String, CodingKey { case title, updatedAt, number, permalink, state, createdAt, closedAt, mergedAt, mergeable, author, body, headRefOid, reviewRequests, assignees, comments, reviews, commits } } +// MARK: - PullRequstPaginatedCommits Starting Point +struct DecodePaginatedCommitsStartingPoint: Codable { + let data: IntermediaryPaginatedCommitsStruct + + enum CodingKeys: String, CodingKey { + case data + } +} + +// MARK: - IntermediaryStruct +struct IntermediaryPaginatedCommitsStruct: Codable { + let repository: Repository_Temp_Paginated_Commits + + enum CodingKeys: String, CodingKey { + case repository + } +} + +// MARK: - Repository_Temp +struct Repository_Temp_Paginated_Commits: Codable { + let pullRequest: PullRequestPaginatedCommits_Temp + + enum CodingKeys: String, CodingKey { + case pullRequest + } +} + +// MARK: - Repository_Temp +struct PullRequestPaginatedCommits_Temp: Codable { + var commits: PullRequestCommitConnection + + enum CodingKeys: String, CodingKey { + case commits + } +} + + // MARK: - PullRequestState: Confirmed enum PullRequestState: String, Codable { case open = "OPEN" @@ -164,7 +209,15 @@ struct CommentsNode: Codable { // MARK: - PullRequestCommitConnection: Confirmed struct PullRequestCommitConnection: Codable { - let nodes: [CommitNode] + let totalCount: Int + let pageInfo: PageInfo + var nodes: [CommitNode] +} + +struct PageInfo: Codable { + let hasPreviousPage: Bool + let endCursor: String? + let startCursor: String? } // MARK: - CommitNode: Confirmed diff --git a/GraphQLNetworkTester/Queries.swift b/GraphQLNetworkTester/Queries.swift index 1302d35..faeedc9 100644 --- a/GraphQLNetworkTester/Queries.swift +++ b/GraphQLNetworkTester/Queries.swift @@ -80,6 +80,12 @@ extension GraphQLQuery { } } commits(last: 100) { + totalCount + pageInfo { + endCursor + hasPreviousPage + startCursor + } nodes { commit { author { @@ -202,4 +208,41 @@ extension GraphQLQuery { """ ) } + + static func getPaginatedCommitData(forOwner owner: String, inRepo repo: String, withNumber number: Int, forStartCursor startCursor: String) -> GraphQLQuery { + return GraphQLQuery( + query: """ + query { + repository(owner: "\(owner)", name: "\(repo)"){ + pullRequest(number: \(number)) { + commits(last: 100, before: "\(startCursor)") { + totalCount + pageInfo { + endCursor + hasPreviousPage + startCursor + } + nodes { + commit { + author { + name + avatarUrl + user { + avatarUrl + login + } + } + authoredDate + message + commitUrl + oid + } + } + } + } + } + } + """ + ) + } } diff --git a/GraphQLNetworkTester/RepoFetchObject.swift b/GraphQLNetworkTester/RepoFetchObject.swift new file mode 100644 index 0000000..64d3198 --- /dev/null +++ b/GraphQLNetworkTester/RepoFetchObject.swift @@ -0,0 +1,41 @@ +// +// RepoFetchObject.swift +// GraphQLNetworkTester +// +// Created by Dachary Carey on 9/12/23. +// + +import Foundation + +class RepoFetchObject { + var repoOwner: String + var repoName: String + var repoId: String + var pullRequestNumbersToFetch: [Int] + + init(repoOwner: String, repoName: String, repoId: String, pullRequestNumbersToFetch: [Int]) { + self.repoOwner = repoOwner + self.repoName = repoName + self.repoId = repoId + self.pullRequestNumbersToFetch = pullRequestNumbersToFetch + } +} + +class RepoFetchResult { + var repoFetchObject: RepoFetchObject + var prObjects: [PullRequest_Temp] = [] + var prStatusChecks: [StatusCheckPullRequest_Temp] = [] + + init(repoFetchObject: RepoFetchObject) { + self.repoFetchObject = repoFetchObject + } +} + +extension RepoFetchObject { + static let realmSwift = RepoFetchObject(repoOwner: "realm", repoName: "realm-swift", repoId: "Some string", pullRequestNumbersToFetch: [3578,4744,4827,4923,5686,6021,6041,6533,6746,7050,7160,7229,7301,7448,7482,7498,7686,7715,7722,7724,7742,7755,8004,8034,8054,8100,8109,8136,8142,8153,8244,8249,8261,8295,8319,8324,8334,8335,8344]) + static let docsRealm = RepoFetchObject(repoOwner: "mongodb", repoName: "docs-realm", repoId: "another sstring", pullRequestNumbersToFetch: [2524,2823,2877,2943,2966,2974,2979,3000,3001,3002,3003,3005,3009,3010]) + static let docsAppServices = RepoFetchObject(repoOwner: "mongodb", repoName: "docs-app-services", repoId: "the best string", pullRequestNumbersToFetch: [183,548,577]) + static let realmKotlin = RepoFetchObject(repoOwner: "realm", repoName: "realm-kotlin", repoId: "my favorite string", pullRequestNumbersToFetch: [881,1272,1429,1504,1510,1514]) + static let realmJS = RepoFetchObject(repoOwner: "realm", repoName: "realm-js", repoId: "dsodsm", pullRequestNumbersToFetch: [2018,2666,2733,3825,3935,4019,4027,4030,4061,4153,4316,4376,4544,4559,4678,4769,4770,4871,4942,5053,5057,5065,5081,5369,5384,5448,5452,5479,5482,5525,5528,5621,5672,5691,5692,5694,5754,5861,5866,5895,5908,5931,5934,5943,5998,6032,6038,6067,6116,6118,6121,6125,6126]) + static let realmCpp = RepoFetchObject(repoOwner: "realm", repoName: "realm-cpp", repoId: "dasjlkds", pullRequestNumbersToFetch: [19,24,33,35,38,39,40,45,91,92,93,94]) +}