@@ -21,28 +21,120 @@ internal class CollectAPICallback: Callback {
2121 self . options = options
2222 self . contextOptions = contextOptions
2323 }
24+
2425 internal func onSuccess( _ responseBody: Any ) {
25- guard let url = URL ( string: self . apiClient. vaultURL + self . apiClient. vaultID) else {
26- self . callback. onFailure ( ErrorCodes . INVALID_URL ( ) . getErrorObject ( contextOptions: self . contextOptions) )
26+ let insertRecords = records [ " records " ] as? [ [ String : Any ] ]
27+ let hasInsert = insertRecords? . isEmpty == false
28+ let updateRecords = records [ " update " ] as? [ String : Any ]
29+ let hasUpdate = updateRecords? . isEmpty == false
30+ let group = DispatchGroup ( )
31+ var insertResponse : [ String : Any ] ? = nil
32+ var updateResponses : [ [ String : Any ] ] = [ ]
33+ var requestError : [ Any ] ? = [ ]
34+ var requestUpdateError : [ Any ] ? = [ ]
35+
36+ let callbackQueue = DispatchQueue . main
37+
38+ if !hasInsert && !hasUpdate {
39+ self . callback. onSuccess ( [ : ] )
2740 return
2841 }
29-
30- do {
31- let ( request, session) = try self . getRequestSession ( url: url)
32-
33-
34- let task = session. dataTask ( with: request) { data, response, error in
42+
43+ if hasInsert {
44+ group. enter ( )
45+ guard let url = URL ( string: self . apiClient. vaultURL + self . apiClient. vaultID) else {
46+ self . callback. onFailure ( ErrorCodes . INVALID_URL ( ) . getErrorObject ( contextOptions: self . contextOptions) )
47+ return
48+ }
49+ do {
50+ let ( request, session) = try self . getRequestSession ( url: url)
51+ let task = session. dataTask ( with: request) { data, response, error in
52+ defer {
53+ group. leave ( )
54+ }
55+ do {
56+ let response = try self . processResponse ( data: data, response: response, error: error)
57+ if response [ " error " ] != nil {
58+ requestError? . append ( response)
59+ } else {
60+ insertResponse = response
61+ }
62+ } catch {
63+ requestError? . append ( error)
64+ }
65+ }
66+ task. resume ( )
67+ } catch let error {
68+ requestError? . append ( error)
69+ }
70+ }
71+ if hasUpdate, let updateArray = records [ " update " ] as? [ String : [ String : Any ] ] {
72+ for (_, updateRecord) in updateArray {
73+ group. enter ( )
74+ guard let table = updateRecord [ " table " ] as? String , let skyflowID = updateRecord [ " skyflowID " ] as? String else {
75+ group. leave ( )
76+ continue
77+ }
78+ let urlString = self . apiClient. vaultURL + self . apiClient. vaultID + " / " + table + " / " + skyflowID
79+ guard let url = URL ( string: urlString) else {
80+ group. leave ( )
81+ requestError? . append ( ErrorCodes . INVALID_URL ( ) . getErrorObject ( contextOptions: self . contextOptions) )
82+ continue
83+ }
3584 do {
36- let response = try self . processResponse ( data: data, response: response, error: error)
37- self . callback. onSuccess ( response)
38- } catch {
39- self . callback. onFailure ( error)
85+ var singleUpdateRecords : [ String : Any ] = [ : ]
86+ singleUpdateRecords [ " fields " ] = updateRecord [ " fields " ]
87+ singleUpdateRecords [ " table " ] = table
88+ singleUpdateRecords [ " skyflowID " ] = skyflowID
89+ let ( request, session) = try self . getRequestSessionForUpdate ( url: url, updateRecords: singleUpdateRecords)
90+ let task = session. dataTask ( with: request) { data, response, error in
91+ defer {
92+ group. leave ( ) }
93+ do {
94+ let response = try self . processUpdateResponse ( data: data, response: response, error: error, table: table)
95+ if response [ " error " ] != nil {
96+ requestUpdateError? . append ( response)
97+ } else {
98+ updateResponses. append ( response)
99+ }
100+ } catch {
101+ requestUpdateError? . append ( error)
102+ }
103+ }
104+ task. resume ( )
105+ } catch let error {
106+ group. leave ( )
107+ requestUpdateError? . append ( error)
108+ continue
40109 }
41110 }
42- task. resume ( )
43- } catch let error {
44- self . callback. onFailure ( error)
45- return
111+ }
112+
113+ group. notify ( queue: callbackQueue) {
114+ var mergedRecords : [ Any ] = [ ]
115+ var mergedErrors : [ Any ] = [ ]
116+ if let insert = insertResponse ? [ " records " ] as? [ Any ] {
117+ mergedRecords. append ( contentsOf: insert)
118+ }
119+ for updateResp in updateResponses {
120+ if let update = updateResp [ " records " ] as? [ Any ] {
121+ mergedRecords. append ( contentsOf: update)
122+ }
123+ }
124+ if requestUpdateError != nil {
125+ mergedErrors. append ( contentsOf: requestUpdateError!)
126+ }
127+ if requestError != nil {
128+ mergedErrors. append ( contentsOf: requestError!)
129+ }
130+ if mergedRecords. isEmpty {
131+ self . callback. onFailure ( [ " errors " : mergedErrors] )
132+ } else if requestError? . isEmpty == true {
133+ self . callback. onSuccess ( [ " records " : mergedRecords] )
134+ }
135+ if !mergedErrors. isEmpty && !mergedRecords. isEmpty {
136+ self . callback. onFailure ( [ " records " : mergedRecords, " errors " : mergedErrors] )
137+ }
46138 }
47139 }
48140
@@ -86,31 +178,104 @@ internal class CollectAPICallback: Callback {
86178
87179 }
88180
181+ // Helper for single update request
182+ private func getRequestSessionForUpdate( url: URL , updateRecords: [ String : Any ] ) throws -> ( URLRequest , URLSession ) {
183+ var jsonString = " "
184+ do {
185+ let deviceDetails = FetchMetrices ( ) . getMetrices ( )
186+ let jsonData = try JSONSerialization . data ( withJSONObject: deviceDetails, options: [ ] )
187+ jsonString = String ( data: jsonData, encoding: . utf8) ?? " "
188+ } catch {
189+ jsonString = " "
190+ }
191+ var request = URLRequest ( url: url)
192+ request. httpMethod = " PUT "
193+ do {
194+ let data = try JSONSerialization . data ( withJSONObject: self . apiClient. constructUpdateRequestBody ( records: updateRecords, options: options) )
195+ request. httpBody = data
196+ }
197+ request. setValue ( ( " Bearer " + self . apiClient. token) , forHTTPHeaderField: " Authorization " )
198+ request. setValue ( jsonString, forHTTPHeaderField: " sky-metadata " )
199+ return ( request, URLSession ( configuration: . default) )
200+ }
201+
202+ func processUpdateResponse( data: Data ? , response: URLResponse ? , error: Error ? , table: String ) throws -> [ String : Any ] {
203+ if error != nil || response == nil {
204+ return [ " error " : [ " message " : ( error) ? . localizedDescription ?? " Unknown error " ] ]
205+ }
206+ if let httpResponse = response as? HTTPURLResponse {
207+ let range = 400 ... 599
208+ if range ~= httpResponse. statusCode {
209+ var description = " Update call failed with the following status code " + String( httpResponse. statusCode)
210+ if let safeData = data {
211+ do {
212+ let desc = try JSONSerialization . jsonObject ( with: safeData, options: . allowFragments) as! [ String : Any ]
213+ let error = desc [ " error " ] as? [ String : Any ]
214+ if let error = error, let message = error [ " message " ] as? String {
215+ description = message
216+ }
217+ if let requestId = httpResponse. allHeaderFields [ " x-request-id " ] {
218+ description += " - request-id: \( requestId) "
219+ }
220+ } catch {
221+ return [ " error " : [ " message " : String ( data: safeData, encoding: . utf8) ?? " Unknown error " , " code " : httpResponse. statusCode] ]
222+ }
223+ }
224+ return [ " error " : [ " message " : description, " code " : httpResponse. statusCode] ]
225+ }
226+ }
227+ guard let safeData = data else {
228+ return [ " records " : [ ] ]
229+ }
230+ let jsonData = try JSONSerialization . jsonObject ( with: safeData, options: . allowFragments) as! [ String : Any ]
231+ var record : [ String : Any ] = [ : ]
232+ var id = " "
233+ if let skyflowId = jsonData [ " skyflow_id " ] as? String {
234+ id = skyflowId
235+ } else {
236+ id = String ( describing: jsonData [ " skyflow_id " ] ?? " " )
237+ }
238+
239+ if self . options. tokens {
240+ let fieldsDict = jsonData [ " tokens " ] as? [ String : Any ]
241+ if fieldsDict != nil {
242+ let fieldsData = try JSONSerialization . data ( withJSONObject: fieldsDict!)
243+ let fieldsObj = try JSONSerialization . jsonObject ( with: fieldsData, options: . allowFragments)
244+ var fieldsSkyflowId : [ String : Any ] = self . buildFieldsDict ( dict: fieldsObj as? [ String : Any ] ?? [ : ] )
245+ fieldsSkyflowId [ " skyflow_id " ] = id
246+ record [ " fields " ] = fieldsSkyflowId
247+ }
248+ } else {
249+ record [ " skyflow_id " ] = id
250+ }
251+ record [ " table " ] = table
252+ return [ " records " : [ record] ]
253+ }
254+
89255 func processResponse( data: Data ? , response: URLResponse ? , error: Error ? ) throws -> [ String : Any ] {
90256 if error != nil || response == nil {
91- throw error!
257+ return [ " error " : [ " message " : ( error ) ? . localizedDescription ?? " Unknown error " ] ]
92258 }
93259
94260 if let httpResponse = response as? HTTPURLResponse {
95261 let range = 400 ... 599
96262 if range ~= httpResponse. statusCode {
97- var description = " Insert call failed with the following status code " + String( httpResponse. statusCode)
98- var errorObject : Error = ErrorCodes . APIError ( code: httpResponse. statusCode, message: description) . getErrorObject ( contextOptions: self . contextOptions)
99-
263+ var description = " Insert call failed with the following status code " + String( httpResponse. statusCode)
100264 if let safeData = data {
101265 do {
102- let desc = try JSONSerialization . jsonObject ( with: safeData, options: . allowFragments) as! [ String : Any ]
103- let error = desc [ " error " ] as! [ String : Any ]
104- description = error [ " message " ] as! String
266+ let errorResponse = try JSONSerialization . jsonObject ( with: safeData, options: . allowFragments) as! [ String : Any ]
267+ if let errorDetails = errorResponse [ " error " ] as? [ String : Any ] ,
268+ let message = errorDetails [ " message " ] as? String {
269+ description = message
270+ }
105271 if let requestId = httpResponse. allHeaderFields [ " x-request-id " ] {
106272 description += " - request-id: \( requestId) "
107273 }
108- errorObject = ErrorCodes . APIError ( code: httpResponse. statusCode, message: description) . getErrorObject ( contextOptions: self . contextOptions)
109274 } catch {
110- errorObject = ErrorCodes . APIError ( code : httpResponse . statusCode , message: String ( data: safeData, encoding: . utf8) ! ) . getErrorObject ( contextOptions : self . contextOptions )
275+ return [ " error " : [ " message " : String ( data: safeData, encoding: . utf8) ?? " Unknown error " , " code " : httpResponse . statusCode ] ]
111276 }
112277 }
113- throw errorObject
278+ return [ " error " : [ " message " : description , " code " : httpResponse . statusCode ] ]
114279 }
115280 }
116281
0 commit comments