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

Posted

技术标签:

【中文标题】如何获取 Xcode 服务器代码覆盖率 API JSON 响应【英文标题】:How to get Xcode server Code coverage api JSON response 【发布时间】:2017-07-19 09:20:07 【问题描述】:

当我尝试通过传递集成 ID 来访问 Xcode 服务器代码覆盖 API 时,它不是 JSON 响应,而是直接下载一个 .bz2 文件。我想使用此 API 在我的自定义仪表板中显示文件明智的覆盖率报告。

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

【问题讨论】:

【参考方案1】:

不幸的是,API 只返回 .bz2 压缩的 JSON 文件。 即使指定 Accept=application/json 的 HTTP 标头。

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

以下是使用框架BZipCompression 解压缩数据流的 ios/swift 上的示例:

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 框架。我将使用这个确切的解决方案添加代码覆盖率端点。

【讨论】:

我正在尝试在基于角度/离子的 Web 应用程序中使用此代码覆盖率 API 以将其显示在仪表板中。我需要这个 JSON 数据,而不是在我的 iOS 应用程序中。在这种情况下我应该怎么做?我是否应该让我的网络开发人员下载这个 .bz2 并将其转换为 JSON 以显示数据? 您可以使用 CompressJS (github.com/cscott/compressjs) 之类的东西在 javascript 中进行解压缩。解压缩数据后,您应该拥有可以与之交互的有效 JSON。请记住,代码覆盖率 API 将为任何没有数据的集成返回 404 状态代码。

以上是关于如何获取 Xcode 服务器代码覆盖率 API JSON 响应的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xcode 7 中使用代码覆盖率?

如何从Xcode中的代码覆盖范围中排除Pod

来自命令行的 Xcode 代码覆盖率

如何在 XCode 9.3 上收集覆盖率数据?

使用Xcode / Swift或独角兽精灵灰尘在iPad上覆盖(阻止)或关闭iOS

如何覆盖由 IdentityServer3.accesstokenvalidation 引起的服务耦合