mirror of
https://github.com/AU-COVIDSafe/mobile-ios.git
synced 2025-01-19 09:16:34 +00:00
156 lines
4.9 KiB
Swift
156 lines
4.9 KiB
Swift
// Copyright © 2020 Australian Government All rights reserved.
|
|
|
|
import UIKit
|
|
|
|
open class HTTPPostFeedbackAction: AsyncAction {
|
|
let issue: Issue
|
|
let screenshotImageOrNil: UIImage?
|
|
let target: JMCTarget
|
|
let onComplete: (Outcome<Void>) -> Void
|
|
var issueData: Data!
|
|
var customFieldsData: Data!
|
|
|
|
var issueJSONInfo: [String: NSObject] {
|
|
// Any other interesting pieces of data to grab?
|
|
let issueInfo = issue.infoAsDictionary
|
|
let deviceInfo = UIDevice.current.infoAsDictionary
|
|
let languageInfo = NSLocale.infoAsDictionary
|
|
let bundleInfo = Foundation.Bundle.main.infoAsDictionary
|
|
return [issueInfo, deviceInfo, languageInfo, bundleInfo].reduce([:], +)
|
|
}
|
|
|
|
var customFieldsDataOrNil: Data? {
|
|
if issue.customFields.isEmpty {
|
|
return nil
|
|
} else {
|
|
return customFieldsData
|
|
}
|
|
}
|
|
|
|
var screenshotImageDataOrNil: Data? {
|
|
switch screenshotImageOrNil {
|
|
case .some(let screenshotImage):
|
|
return screenshotImage.jpegData(compressionQuality: 1.0)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public init(issue: Issue, screenshotImageOrNil: UIImage? = nil, target: JMCTarget, onComplete: @escaping (Outcome<Void>) -> Void) {
|
|
self.issue = issue
|
|
self.screenshotImageOrNil = screenshotImageOrNil
|
|
self.target = target
|
|
self.onComplete = onComplete
|
|
super.init()
|
|
}
|
|
|
|
override open func run() {
|
|
// IMPORTANT: Encoding memory threshold is 10 mb by default.
|
|
do {
|
|
issueData = try serializeIssueData()
|
|
customFieldsData = try serializeCustomFieldsData()
|
|
} catch {
|
|
finishedExecutingOperationWithOutcome(.error(error))
|
|
return
|
|
}
|
|
|
|
guard let url = URL(string: target.postIssueURLString) else {
|
|
assertionFailure("Cannot create URL from host & path")
|
|
return
|
|
}
|
|
|
|
let boundary = UUID().uuidString
|
|
var request = URLRequest(url: url)
|
|
var data = Data()
|
|
|
|
data.append(multipartFormData: issueData, withName: "issue", fileName: "issue.json", boundary: boundary, mimeType: "application/json")
|
|
|
|
if let customFieldData = customFieldsDataOrNil {
|
|
data.append(multipartFormData: customFieldData, withName: "customfields", fileName: "customfields.json", boundary: boundary, mimeType: "application/json")
|
|
}
|
|
|
|
if let screenshotImageData = screenshotImageDataOrNil {
|
|
data.append(multipartFormData: screenshotImageData, withName: "screenshot", fileName: "screenshot.jpg", boundary: boundary, mimeType: "image/jpeg")
|
|
}
|
|
|
|
data.appendString("--\(boundary)--\r\n")
|
|
|
|
request.httpMethod = "POST"
|
|
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
|
request.setValue("\(data.count)", forHTTPHeaderField:"Content-Length")
|
|
request.setValue("-x-jmc-requestid", forHTTPHeaderField: UIDevice.current.identifierForVendorString)
|
|
request.httpBody = data
|
|
|
|
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
|
DispatchQueue.main.async {
|
|
self.onHTTPResponse(response as? HTTPURLResponse)
|
|
}
|
|
}
|
|
|
|
dataTask.resume()
|
|
}
|
|
|
|
func onHTTPResponse(_ HTTPResponseOrNil: HTTPURLResponse?) {
|
|
guard let HTTPResponse = HTTPResponseOrNil else {
|
|
finishedExecutingOperationWithOutcome(.error(JMCError.nilHTTPResponseError))
|
|
return
|
|
}
|
|
|
|
switch HTTPResponse.statusCode {
|
|
case let statusCode where statusCode.isSuccessHTTPStatuCode:
|
|
finishedExecutingOperationWithOutcome(.success(()))
|
|
default:
|
|
finishedExecutingOperationWithOutcome(.error(JMCError.httpResponseError))
|
|
}
|
|
}
|
|
|
|
func serializeIssueData() throws -> Data {
|
|
return try JSONSerialization.data(withJSONObject: issueJSONInfo, options: [])
|
|
}
|
|
|
|
func serializeCustomFieldsData() throws -> Data {
|
|
return try JSONSerialization.data(withJSONObject: issue.customFields, options: [])
|
|
}
|
|
|
|
func finishedExecutingOperationWithOutcome(_ outcome: Outcome<Void>) {
|
|
finishedExecutingOperation()
|
|
onComplete(outcome)
|
|
}
|
|
}
|
|
|
|
extension JMCTarget {
|
|
var postIssueURLString: String {
|
|
return "https://" + host + "/" + "rest/jconnect/" + "1.0" + "/issue/create?" + "project=" + projectKey + "&apikey=" + apiKey
|
|
}
|
|
}
|
|
|
|
extension Int {
|
|
fileprivate var isSuccessHTTPStatuCode: Bool {
|
|
return 200...299 ~= self
|
|
}
|
|
}
|
|
|
|
// Add function for combining dictionaries.
|
|
private func + <K, V>(lhs: [K : V], rhs: [K : V]) -> [K : V] {
|
|
var combined = lhs
|
|
|
|
for (k, v) in rhs {
|
|
combined[k] = v
|
|
}
|
|
|
|
return combined
|
|
}
|
|
|
|
extension Data {
|
|
mutating func append(multipartFormData data: Data, withName name: String, fileName: String, boundary: String, mimeType: String) {
|
|
appendString("--\(boundary)\r\n")
|
|
appendString("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n")
|
|
appendString("Content-Type: \(mimeType)\r\n\r\n")
|
|
append(data)
|
|
appendString("\r\n")
|
|
}
|
|
|
|
mutating func appendString(_ string: String) {
|
|
append(string.data(using: .utf8)!)
|
|
}
|
|
}
|