// // EventSerializer.swift // PiwikTracker // // Created by Cornelius Horstmann on 11.01.18. // Copyright © 2018 PIWIK. All rights reserved. // import Foundation final class EventSerializer { internal func jsonData(for events: [Event]) throws -> Data { let eventsAsQueryItems = events.map({ $0.queryItems }) let serializedEvents = eventsAsQueryItems.map({ items in items.flatMap({ item in guard let value = item.value, let encodedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryParameterAllowed) else { return nil } return "\(item.name)=\(encodedValue)" }).joined(separator: "&") }) let body = ["requests": serializedEvents.map({ "?\($0)" })] return try JSONSerialization.data(withJSONObject: body, options: []) } } fileprivate extension Event { private func customVariableParameterValue() -> String { let customVariableParameterValue: [String] = customVariables.map { "\"\($0.index)\":[\"\($0.name)\",\"\($0.value)\"]" } return "{\(customVariableParameterValue.joined(separator: ","))}" } var queryItems: [URLQueryItem] { get { let items = [ URLQueryItem(name: "idsite", value: siteId), URLQueryItem(name: "rec", value: "1"), // Visitor URLQueryItem(name: "_id", value: visitor.id), URLQueryItem(name: "uid", value: visitor.userId), // Session URLQueryItem(name: "_idvc", value: "\(session.sessionsCount)"), URLQueryItem(name: "_viewts", value: "\(Int(session.lastVisit.timeIntervalSince1970))"), URLQueryItem(name: "_idts", value: "\(Int(session.firstVisit.timeIntervalSince1970))"), URLQueryItem(name: "url", value:url?.absoluteString), URLQueryItem(name: "action_name", value: actionName.joined(separator: "/")), URLQueryItem(name: "lang", value: language), URLQueryItem(name: "urlref", value: referer?.absoluteString), URLQueryItem(name: "new_visit", value: isNewSession ? "1" : nil), URLQueryItem(name: "h", value: DateFormatter.hourDateFormatter.string(from: date)), URLQueryItem(name: "m", value: DateFormatter.minuteDateFormatter.string(from: date)), URLQueryItem(name: "s", value: DateFormatter.secondsDateFormatter.string(from: date)), //screen resolution URLQueryItem(name: "res", value:String(format: "%1.0fx%1.0f", screenResolution.width, screenResolution.height)), URLQueryItem(name: "e_c", value: eventCategory), URLQueryItem(name: "e_a", value: eventAction), URLQueryItem(name: "e_n", value: eventName), URLQueryItem(name: "e_v", value: eventValue != nil ? "\(eventValue!)" : nil), URLQueryItem(name: "_rcn", value: campaignName), URLQueryItem(name: "_rck", value: campaignKeyword), URLQueryItem(name: "search", value: searchQuery), URLQueryItem(name: "search_cat", value: searchCategory), URLQueryItem(name: "search_count", value: searchResultsCount != nil ? "\(searchResultsCount!)" : nil), URLQueryItem(name: "c_n", value: contentName), URLQueryItem(name: "c_p", value: contentPiece), URLQueryItem(name: "c_t", value: contentTarget), URLQueryItem(name: "c_i", value: contentInteraction), ].filter { $0.value != nil } let dimensionItems = dimensions.map { URLQueryItem(name: "dimension\($0.index)", value: $0.value) } let customItems = customTrackingParameters.map { return URLQueryItem(name: $0.key, value: $0.value) } let customVariableItems = customVariables.count > 0 ? [URLQueryItem(name: "_cvar", value: customVariableParameterValue())] : [] return items + dimensionItems + customItems + customVariableItems } } } fileprivate extension DateFormatter { static let hourDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "HH" return dateFormatter }() static let minuteDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "mm" return dateFormatter }() static let secondsDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "ss" return dateFormatter }() } fileprivate extension CharacterSet { /// Returns the character set for characters allowed in a query parameter URL component. fileprivate static var urlQueryParameterAllowed: CharacterSet { return CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&/?")) } }