@@ -29,59 +29,108 @@ final class MockBundle {
29
29
/// - Returns: The MockRequestResponse, if it can be loaded
30
30
func loadResponse( for requestResponse: MockRequestResponse ) -> Bool {
31
31
guard let fileName = requestResponse. fileName ( for: . request) else { return false }
32
-
33
- var targetURL : URL ?
34
- var targetLoadingURL : URL ?
35
32
let request = requestResponse. request
36
33
34
+ var loadedPath : String ?
35
+ var loadedResponse : MockResponse ?
37
36
if let response = checkRequestHandlers ( for: request) {
38
- requestResponse. responseWrapper = response
39
- return true
40
- } else if
41
- let inputURL = loadingURL? . appendingPathComponent ( fileName) ,
42
- FileManager . default. fileExists ( atPath: inputURL. path)
43
- {
44
- os_log ( " Loading request %@ from: %@ " , log: MockDuck . log, type: . debug, " \( request) " , inputURL. path)
45
- targetURL = inputURL
46
- targetLoadingURL = loadingURL
47
- } else if
48
- let inputURL = recordingURL? . appendingPathComponent ( fileName) ,
49
- FileManager . default. fileExists ( atPath: inputURL. path)
50
- {
51
- os_log ( " Loading request %@ from: %@ " , log: MockDuck . log, type: . debug, " \( request) " , inputURL. path)
52
- targetURL = inputURL
53
- targetLoadingURL = recordingURL
37
+ loadedResponse = response
38
+ } else if let response = loadResponseFile ( relativePath: fileName, baseURL: loadingURL) {
39
+ loadedPath = loadingURL? . path ?? " " + fileName
40
+ loadedResponse = response. responseWrapper
41
+ } else if let response = loadResponseFile ( relativePath: fileName, baseURL: recordingURL) {
42
+ loadedPath = recordingURL? . path ?? " " + fileName
43
+ loadedResponse = response. responseWrapper
54
44
} else {
55
45
os_log ( " Request %@ not found on disk. Expected file name: %@ " , log: MockDuck . log, type: . debug, " \( request) " , fileName)
56
46
}
57
-
58
- if
59
- let targetURL = targetURL,
60
- let targetLoadingURL = targetLoadingURL
61
- {
62
- let decoder = JSONDecoder ( )
63
-
64
- do {
65
- let data = try Data ( contentsOf: targetURL)
66
-
67
- let loaded = try decoder. decode ( MockRequestResponse . self, from: data)
68
- requestResponse. responseWrapper = loaded. responseWrapper
69
-
70
- // Load the response data if the format is supported.
71
- // This should be the same filename with a different extension.
72
- if let dataFileName = requestResponse. fileName ( for: . responseData) {
73
- let dataURL = targetLoadingURL. appendingPathComponent ( dataFileName)
74
- requestResponse. responseData = try Data ( contentsOf: dataURL)
75
- }
76
-
77
- return true
78
- } catch {
79
- os_log ( " Error decoding JSON: %@ " , log: MockDuck . log, type: . error, " \( error) " )
47
+
48
+ if let response = loadedResponse {
49
+ requestResponse. responseWrapper = response
50
+ if let path = loadedPath {
51
+ os_log ( " Loading request %@ from: %@ " ,
52
+ log: MockDuck . log,
53
+ type: . debug,
54
+ " \( request) " ,
55
+ path)
80
56
}
57
+ return true
81
58
}
82
-
83
59
return false
84
60
}
61
+
62
+ /// Takes a URL and attempts to parse the file at that location into a MockRequestResponse
63
+ /// If the file doesn't exist, or isn't in the expected MockDuck format, nil is returned
64
+ ///
65
+ /// - Parameter targetURL: URL that should be loaded from file
66
+ /// - Returns: MockRequestResponse if the request exists at that URL
67
+ func loadResponseFile( relativePath: String , baseURL: URL ? ) -> MockRequestResponse ? {
68
+ guard let baseURL = baseURL else { return nil }
69
+ let targetURL = baseURL. appendingPathComponent ( relativePath)
70
+ guard FileManager . default. fileExists ( atPath: targetURL. path) else { return nil }
71
+
72
+ let decoder = JSONDecoder ( )
73
+
74
+ do {
75
+ let data = try Data ( contentsOf: targetURL)
76
+
77
+ let response = try decoder. decode ( MockRequestResponse . self, from: data)
78
+
79
+ // Load the response data if the format is supported.
80
+ // This should be the same filename with a different extension.
81
+ if let dataFileName = response. fileName ( for: . responseData) {
82
+ let dataURL = baseURL. appendingPathComponent ( dataFileName)
83
+ response. responseData = try ? Data ( contentsOf: dataURL)
84
+ }
85
+
86
+ return response
87
+ } catch {
88
+ os_log ( " Error decoding JSON: %@ " , log: MockDuck . log, type: . error, " \( error) " )
89
+ }
90
+ return nil
91
+ }
92
+
93
+ /// Takes a passed in hostname and returns all the recorded mocks for that URL.
94
+ /// If an empty string is passed in, all recordings will be returned.
95
+ ///
96
+ /// - Parameter hostname: String representing the hostname to load requests from.
97
+ /// - Returns: An array of MockRequestResponse for each request under that domain
98
+ func getResponses( for hostname: String ) -> [ MockRequestResponse ] {
99
+ guard let recordingURL = recordingURL else { return [ ] }
100
+
101
+ let baseURL = recordingURL. resolvingSymlinksInPath ( )
102
+ var responses = [ MockRequestResponse] ( )
103
+ let targetURL = baseURL. appendingPathComponent ( hostname)
104
+
105
+ let results = FileManager . default. enumerator (
106
+ at: targetURL,
107
+ includingPropertiesForKeys: [ . isDirectoryKey] ,
108
+ options: [ ] )
109
+
110
+ if let results = results {
111
+ for case let item as URL in results {
112
+ var isDir = ObjCBool ( false )
113
+ let itemURL = item. resolvingSymlinksInPath ( )
114
+
115
+ /// Check if the item:
116
+ /// 1) isn't a directory
117
+ /// 2) doesn't end in '-response' (a sidecar file)
118
+ /// If so, load it using loadResponseFile so any associated
119
+ /// '-response' file is also loaded with the repsonse.
120
+ if
121
+ FileManager . default. fileExists ( atPath: itemURL. path, isDirectory: & isDir) ,
122
+ !isDir. boolValue,
123
+ !itemURL. lastPathComponent. contains ( " -response " ) ,
124
+ let relativePath = itemURL. pathRelative ( to: baseURL) ,
125
+ let response = loadResponseFile ( relativePath: relativePath, baseURL: recordingURL)
126
+ {
127
+ responses. append ( response)
128
+ }
129
+ }
130
+ }
131
+
132
+ return responses
133
+ }
85
134
86
135
/// If recording is enabled, this method saves the request to the filesystem. If the request
87
136
/// body or the response data are of a certain type 'jpg/png/gif/json', the request is saved
@@ -169,3 +218,24 @@ final class MockBundle {
169
218
}
170
219
}
171
220
}
221
+
222
+
223
+ extension URL {
224
+ func pathRelative( to url: URL ) -> String ? {
225
+ guard
226
+ host == url. host,
227
+ scheme == url. scheme
228
+ else { return nil }
229
+
230
+ let components = self . standardized. pathComponents
231
+ let baseComponents = url. standardized. pathComponents
232
+
233
+ if components. count < baseComponents. count { return nil }
234
+ for (index, baseComponent) in baseComponents. enumerated ( ) {
235
+ let component = components [ index]
236
+ if component != baseComponent { return nil }
237
+ }
238
+
239
+ return components [ baseComponents. count..< components. count] . joined ( separator: " / " )
240
+ }
241
+ }
0 commit comments