如何获取 Xcode 服务器代码覆盖率 api JSON 响应

How to get Xcode server Code coverage api JSON response

当我尝试通过传递集成 ID 来达到 Xcode 服务器代码覆盖率 API 时,它不是 JSON 响应而是直接下载 .bz2 文件。我想使用此 API.

在我的自定义仪表板中显示文件明智的覆盖率报告

有什么方法可以让我从这个 API (https://developer.apple.com/library/content/documentation/Xcode/Conceptual/XcodeServerAPIReference/CodeCoverage.html) 而不是 .bz2 文件得到 JSOn 响应?

不幸的是,API 只有 returns .bz2 压缩的 JSON 文件。 即使指定 HTTP Header of Accept=application/json。

解决此问题的唯一方法是解压缩数据以访问底层 JSON。

下面是一个示例,说明在 iOS/swift 上使用框架 BZipCompression 解压缩数据流可能会是什么样子:

import Foundation
import BZipCompression

public class Coverage {
    public typealias CoverageCompletion = (_: Data?, _: Error?) -> Void

    public enum Errors: Error {
        case invalidURL
        case invalidResponse
        case invalidStatusCode
        case invalidData
    }

    static var session: URLSession {
        let session = URLSession(configuration: URLSessionConfiguration.default, delegate: LocalhostSessionDelegate.default, delegateQueue: nil)
        return session
    }

    static public func coverage(forIntegrationWithIdentifier identifier: String, completion: @escaping CoverageCompletion) {
        guard let url = URL(string: "https://localhost:20343/api/integrations/\(identifier)/coverage") else {
            completion(nil, Errors.invalidURL)
            return
        }

        let request = URLRequest(url: url)
        let task = session.dataTask(with: request) { (data, response, error) in
            guard error == nil else {
                completion(nil, error)
                return
            }

            guard let urlResponse = response as? HTTPURLResponse else {
                completion(nil, Errors.invalidResponse)
                return
            }

            guard urlResponse.statusCode == 200 else {
                completion(nil, Errors.invalidStatusCode)
                return
            }

            guard let d = data else {
                completion(nil, Errors.invalidData)
                return
            }

            var decompressedData: Data
            do {
                decompressedData = try self.decompress(data: d)
            } catch let decompressionError {
                completion(nil, decompressionError)
                return
            }

            completion(decompressedData, nil)
        }
        task.resume()
    }

    static internal func decompress(data: Data) throws -> Data {
        let decompressedData = try BZipCompression.decompressedData(with: data)

        guard let decompressedString = String(data: decompressedData, encoding: .utf8) else {
            throw Errors.invalidData
        }

        guard let firstBrace = decompressedString.range(of: "{") else {
           throw Errors.invalidData
        }

        guard let lastBrace = decompressedString.range(of: "}", options: .backwards, range: nil, locale: nil) else {
            throw Errors.invalidData
        }

        let range = decompressedString.index(firstBrace.lowerBound, offsetBy: 0)..<decompressedString.index(lastBrace.lowerBound, offsetBy: 1)
        let json = decompressedString.substring(with: range)

        guard let validData = json.data(using: .utf8) else {
            throw Errors.invalidData
        }

        return validData
    }
}

/// Class implementing the NSURLSessionDelegate which forcefully bypasses untrusted SSL Certificates.
public class LocalhostSessionDelegate: NSObject, URLSessionDelegate {
    static public var `default` = LocalhostSessionDelegate()

    // MARK: - NSURLSessionDelegate
    @objc open func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard challenge.previousFailureCount < 1 else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        var credentials: URLCredential?
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            if let serverTrust = challenge.protectionSpace.serverTrust {
                credentials = URLCredential(trust: serverTrust)
            }
        }

        completionHandler(.useCredential, credentials)
    }
}

我注意到解压后的数据通常在有效 JSON 块的开头和结尾包含无效的控制字符和其他垃圾。 decompress() 在完成块中返回之前清理数据。

您可能想在 GitHub 上查看我的 swift XCServerAPI 框架。我将使用这个确切的解决方案添加代码覆盖率端点。