@@ -29,59 +29,105 @@ final class MockBundle {
2929 /// - Returns: The MockRequestResponse, if it can be loaded
3030 func loadResponse( for requestResponse: MockRequestResponse ) -> Bool {
3131 guard let fileName = requestResponse. fileName ( for: . request) else { return false }
32-
33- var targetURL : URL ?
34- var targetLoadingURL : URL ?
3532 let request = requestResponse. request
3633
34+ var loadedPath : String ?
35+ var loadedResponse : MockResponse ?
3736 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
5444 } else {
5545 os_log ( " Request %@ not found on disk. Expected file name: %@ " , log: MockDuck . log, type: . debug, " \( request) " , fileName)
5646 }
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)
8056 }
57+ return true
8158 }
82-
8359 return false
8460 }
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 baseURL = recordingURL else { return [ ] }
100+
101+ var responses = [ MockRequestResponse] ( )
102+ let targetURL = baseURL. appendingPathComponent ( hostname)
103+
104+ let results = FileManager . default. enumerator (
105+ at: targetURL,
106+ includingPropertiesForKeys: [ . isDirectoryKey] ,
107+ options: [ ] )
108+ if let results = results {
109+ for item in results {
110+ var isDir = ObjCBool ( false )
111+ /// Check if the item:
112+ /// 1) isn't a directory
113+ /// 2) doesn't end in '-response' (a sidecar file)
114+ /// If so, load it using loadResponseFile so any associated '-response'
115+ /// file is also loaded with the repsonse.
116+ if
117+ let item = item as? URL ,
118+ FileManager . default. fileExists ( atPath: item. path, isDirectory: & isDir) ,
119+ !isDir. boolValue,
120+ !item. lastPathComponent. contains ( " -response " ) ,
121+ let relativePath = item. pathRelative ( to: baseURL) ,
122+ let response = loadResponseFile ( relativePath: relativePath, baseURL: recordingURL)
123+ {
124+ responses. append ( response)
125+ }
126+ }
127+ }
128+
129+ return responses
130+ }
85131
86132 /// If recording is enabled, this method saves the request to the filesystem. If the request
87133 /// body or the response data are of a certain type 'jpg/png/gif/json', the request is saved
@@ -169,3 +215,24 @@ final class MockBundle {
169215 }
170216 }
171217}
218+
219+
220+ extension URL {
221+ func pathRelative( to url: URL ) -> String ? {
222+ guard
223+ host == url. host,
224+ scheme == url. scheme
225+ else { return nil }
226+
227+ let components = self . standardized. pathComponents
228+ let baseComponents = url. standardized. pathComponents
229+
230+ if components. count < baseComponents. count { return nil }
231+ for (index, baseComponent) in baseComponents. enumerated ( ) {
232+ let component = components [ index]
233+ if component != baseComponent { return nil }
234+ }
235+
236+ return components [ baseComponents. count..< components. count] . joined ( separator: " / " )
237+ }
238+ }
0 commit comments