Sources/XCMetricsBackendLib/UploadMetrics/Model/UploadMetricsModel.swift (69 lines of code) (raw):

// Copyright (c) 2020 Spotify AB. // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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 Foundation import Vapor import XCMetricsCommon /// Content of a Multipart request to upload Metrics final class UploadMetricsPayload: Content { /// .xcactivitylog file let log: File /// Extra data needed in JSON format let extraInfo: Data /// Build host information in JSON format let buildHost: Data /// Xcode used during the build in JSON format let xcodeVersion: Data? /// Metadata from the plugins used in JSON format let buildMetadata: Data? } /// Request to upload Metrics sent to the UploadMetricsJob final class UploadMetricsRequest: Codable { /// URL to the log file. Can be local or remote depending on the `LogFileRepository` configured let logURL: URL /// Extra data needed let extraInfo: UploadRequestExtraInfo /// Build host information let buildHost: BuildHost /// Xcode used during the build let xcodeVersion: XcodeVersion? /// Metadata from the plugins used let buildMetadata: BuildMetadata? init(logURL : URL, extraInfo: UploadRequestExtraInfo, buildHost: BuildHost, xcodeVersion: XcodeVersion?, buildMetadata: BuildMetadata?) { self.logURL = logURL self.extraInfo = extraInfo self.buildHost = buildHost self.xcodeVersion = xcodeVersion self.buildMetadata = buildMetadata } /// Transforms a `UploadMetricsPayload` to a `UploadMetricsRequest` by decoding the JSON content to domain models /// - Parameters: /// - logURL: URL where the .xcactivitylog was stored by a `LogFileRepository` /// - requestPayload: a `UploadMetricsRequest` /// - Throws: If the `requestPayload` does not contain valid JSON documents convenience init?(logURL: URL, payload: UploadMetricsPayload) throws { let decoder = JSONDecoder() let extraInfo = try decoder.decode(UploadRequestExtraInfo.self, from: payload.extraInfo.xcm_onlyJsonContent()) let buildHost = try decoder.decode(BuildHost.self, from: payload.buildHost.xcm_onlyJsonContent()) let xcodeVersion: XcodeVersion? if let xcodeVersionData = payload.xcodeVersion?.xcm_onlyJsonContent() { xcodeVersion = try decoder.decode(XcodeVersion.self, from: xcodeVersionData) } else { xcodeVersion = nil } let buildMetadata: BuildMetadata? if let buildMetadataData = payload.buildMetadata?.xcm_onlyJsonContent() { buildMetadata = try decoder.decode(BuildMetadata.self, from: buildMetadataData) } else { buildMetadata = nil } self.init(logURL: logURL, extraInfo: extraInfo, buildHost: buildHost, xcodeVersion: xcodeVersion, buildMetadata: buildMetadata) } } extension ByteBuffer { /// Removes the Content-Type part of an `octet-stream` request content, leaving only the actual file content /// - Returns: The `ByteBuffer` removing the Content-Type part func xcm_onlyFileData() -> ByteBuffer { guard let contentType = "Content-Type: application/octet-stream\r\n\r\n".data(using: .utf8) else { return self } var logData = self logData.moveReaderIndex(to: contentType.count) logData.discardReadBytes() return logData } } extension Data { /// Removes the Content-Type part of a `json` request content, leaving only the actual JSON document /// - Returns: The `Data` removing the Content-Type part func xcm_onlyJsonContent() -> Data { guard let contentType = "Content-Type: application/json\r\n\r\n".data(using: .utf8) else { return self } return self.advanced(by: contentType.count) } }