func get()

in Sources/PackageCollectionGenerator/PackageMetadataProviders/GitHubPackageMetadataProvider.swift [35:118]


    func get(_ packageURL: URL, callback: @escaping (Result<PackageBasicMetadata, Error>) -> Void) {
        guard let baseURL = self.apiURL(packageURL.absoluteString) else {
            return callback(.failure(Errors.invalidGitURL(packageURL)))
        }

        let metadataURL = baseURL
        let readmeURL = baseURL.appendingPathComponent("readme")
        let licenseURL = baseURL.appendingPathComponent("license")

        let sync = DispatchGroup()
        let results = ThreadSafeKeyValueStore<URL, Result<HTTPClientResponse, Error>>()

        // get the main data
        sync.enter()
        var metadataHeaders = HTTPClientHeaders()
        metadataHeaders.add(name: "Accept", value: "application/vnd.github.mercy-preview+json")
        let metadataOptions = self.makeRequestOptions(validResponseCodes: [200, 401, 403, 404])
        let hasAuthorization = metadataOptions.authorizationProvider?(metadataURL) != nil
        self.httpClient.get(metadataURL, headers: metadataHeaders, options: metadataOptions) { result in
            defer { sync.leave() }
            results[metadataURL] = result
            if case .success(let response) = result {
                let apiLimit = response.headers.get("X-RateLimit-Limit").first.flatMap(Int.init) ?? -1
                let apiRemaining = response.headers.get("X-RateLimit-Remaining").first.flatMap(Int.init) ?? -1
                switch (response.statusCode, hasAuthorization, apiRemaining) {
                case (_, _, 0):
                    results[metadataURL] = .failure(Errors.apiLimitsExceeded(metadataURL, apiLimit, apiRemaining))
                case (401, true, _):
                    results[metadataURL] = .failure(Errors.invalidAuthToken(metadataURL))
                case (401, false, _):
                    results[metadataURL] = .failure(Errors.permissionDenied(metadataURL))
                case (403, _, _):
                    results[metadataURL] = .failure(Errors.permissionDenied(metadataURL))
                case (404, _, _):
                    results[metadataURL] = .failure(Errors.notFound(metadataURL))
                case (200, _, _):
                    // if successful, fan out multiple API calls
                    [readmeURL, licenseURL].forEach { url in
                        sync.enter()
                        var headers = HTTPClientHeaders()
                        headers.add(name: "Accept", value: "application/vnd.github.v3+json")
                        let options = self.makeRequestOptions(validResponseCodes: [200])
                        self.httpClient.get(url, headers: headers, options: options) { result in
                            defer { sync.leave() }
                            results[url] = result
                        }
                    }
                default:
                    results[metadataURL] = .failure(Errors.invalidResponse(metadataURL, "Invalid status code: \(response.statusCode)"))
                }
            }
        }

        // process results
        sync.notify(queue: self.httpClient.configuration.callbackQueue) {
            do {
                // check for main request error state
                switch results[metadataURL] {
                case .none:
                    throw Errors.invalidResponse(metadataURL, "Response missing")
                case .some(.failure(let error)):
                    throw error
                case .some(.success(let metadataResponse)):
                    guard let metadata = try metadataResponse.decodeBody(GetRepositoryResponse.self, using: self.decoder) else {
                        throw Errors.invalidResponse(metadataURL, "Empty body")
                    }

                    let readme = try results[readmeURL]?.success?.decodeBody(Readme.self, using: self.decoder)
                    let license = try results[licenseURL]?.success?.decodeBody(License.self, using: self.decoder)

                    let model = PackageBasicMetadata(
                        summary: metadata.description,
                        keywords: metadata.topics,
                        readmeURL: readme?.downloadURL,
                        license: license.flatMap { .init(name: $0.license.spdxID, url: $0.downloadURL) }
                    )

                    callback(.success(model))
                }
            } catch {
                return callback(.failure(error))
            }
        }
    }