diff --git a/CHANGELOG.md b/CHANGELOG.md index a33a515f8..ad1df2f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,20 @@ ## 2.7.0 +### Packaging + * Xcode 13.0 or above and Swift 5.5 or above are now required to build MapboxDirections from source. ([#725](https://github.com/mapbox/mapbox-directions-swift/pull/725), [#727](https://github.com/mapbox/mapbox-directions-swift/pull/727)) + +### Command line tool + +* Removed the `--config` option. Instead, pass in either the path to a JSON configuration file or the full URL to a Mapbox Directions API or Mapbox Map Matching API request. ([#726](https://github.com/mapbox/mapbox-directions-swift/pull/726)) +* When the `MAPBOX_ACCESS_TOKEN` environment variable is unset, the tool exits with an error code instead of crashing. ([#728](https://github.com/mapbox/mapbox-directions-swift/pull/728)) +* The tool now connects to the API endpoint in the `MAPBOX_HOST` environment variable, if specified. ([#728](https://github.com/mapbox/mapbox-directions-swift/pull/728)) + +### Other changes + * Added `Waypoint.allowsSnappingToStaticallyClosedRoad` property to allow snapping the waypoint’s location to a statically (long-term) closed part of a road. ([#721](https://github.com/mapbox/mapbox-directions-swift/pull/721)) +* `RouteOptions(url:)` now returns `nil` if given a Mapbox Map Matching API request URL, and `MatchOptions(url:)` returns `nil` if given a Mapbox Directions API request URL. ([#728](https://github.com/mapbox/mapbox-directions-swift/pull/728)) ## v2.6.0 diff --git a/CommandLineTool.md b/CommandLineTool.md index 4e2ddf22f..aff9a284e 100644 --- a/CommandLineTool.md +++ b/CommandLineTool.md @@ -13,16 +13,26 @@ To run (and build if it wasn't yet) `MapboxDirectionsCLI` and see usage: To run the `MapboxDirectionsCLI` within Xcode, select the `MapboxDirectionsCLI` target and edit the scheme to include arguments passed at launch. +## Configuration + +A [Mapbox access token](https://account.mapbox.com/access-tokens/) is required for some operations. Set the `MAPBOX_ACCESS_TOKEN` environment variable to your access token. + +To connect to an API endpoint other than the default Mapbox API endpoint, set the `MAPBOX_HOST` environment variable to the base URL. + ## Usage and Recipes `mapbox-directions-swift` is a useful tool for mobile quality assurance. This tool can be used to verify a response to ensure proper Directions API integration, get a [GPX](https://wikipedia.org/wiki/GPS_Exchange_Format) trace that can be used in the Xcode Simulator, and convert a Directions API request to an Options object. +### Arguments + +The sole argument is either: + +* The path to a JSON file that contains a serialized `RouteOptions` or `MatchOptions` +* The URL of a Mapbox Directions API or Mapbox Map Matching API request + ### Options `--input` -An optional flag for the filepath to the input JSON. If this flag is not used, `mapbox-directions-swift` will fallback to a Directions API request. To request using specific coordinates, specify coordinates using `--waypoints` or a Directions API request using `--url`. - -`--config` -An optional flag for the filepath to the JSON, containing serialized Options data. +An optional flag for the filepath to the input JSON. If this flag is not used, `mapbox-directions-swift` will fallback to a Directions API request. `--output` An optional flag for the filepath to save the conversion result. If no filepath is provided, the result will output to the shell. If you want a GPX trace that can be easily uploaded to Xcode, provide an output filepath with this flag. diff --git a/Sources/MapboxDirections/DirectionsOptions.swift b/Sources/MapboxDirections/DirectionsOptions.swift index cd8a8bba0..e6636e144 100644 --- a/Sources/MapboxDirections/DirectionsOptions.swift +++ b/Sources/MapboxDirections/DirectionsOptions.swift @@ -258,6 +258,11 @@ open class DirectionsOptions: Codable { self.init(waypoints: waypoints, profileIdentifier: profileIdentifier, queryItems: URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems) + + // Distinguish between Directions API and Map Matching API URLs. + guard url.pathComponents.dropLast().joined(separator: "/").hasSuffix(abridgedPath) else { + return nil + } } diff --git a/Sources/MapboxDirectionsCLI/CodingOperation.swift b/Sources/MapboxDirectionsCLI/CodingOperation.swift index 1bda3968a..e5b48a272 100644 --- a/Sources/MapboxDirectionsCLI/CodingOperation.swift +++ b/Sources/MapboxDirectionsCLI/CodingOperation.swift @@ -5,12 +5,6 @@ import Turf import FoundationNetworking #endif -let accessToken: String? = - ProcessInfo.processInfo.environment["MAPBOX_ACCESS_TOKEN"] ?? - UserDefaults.standard.string(forKey: "MBXAccessToken") -let credentials = Credentials(accessToken: accessToken!) -private let directions = Directions(credentials: credentials) - protocol DirectionsResultsProvider { var directionsResults: [DirectionsResult]? { get } } @@ -28,6 +22,7 @@ class CodingOperation (Data) { + private func response(fetching directionsOptions: OptionsType) -> (Data) { + let directions = Directions(credentials: credentials) + let url = directions.url(forCalculating: directionsOptions) + return response(fetching: url) + } + + private func response(fetching url: URL) -> Data { let semaphore = DispatchSemaphore(value: 0) var responseData: Data! - let url = directions.url(forCalculating: directionsOptions) let urlSession = URLSession(configuration: .ephemeral) let task = urlSession.dataTask(with: url) { (data, response, error) in @@ -146,28 +146,52 @@ class CodingOperation, then set the MAPBOX_ACCESS_TOKEN environment variable to your access token.") + } + + let hostURL: URL? + if let host = ProcessInfo.processInfo.environment["MAPBOX_HOST"] ?? + UserDefaults.standard.string(forKey: "MGLMapboxAPIBaseURL") { + hostURL = URL(string: host) + } else { + hostURL = nil + } + + return Credentials(accessToken: accessToken, host: hostURL) + } + } + static var configuration = CommandConfiguration( commandName: "mapbox-directions-swift", abstract: "'mapbox-directions-swift' is a command line tool, designed to round-trip an arbitrary, JSON-formatted Directions or Map Matching API response through model objects and back to JSON.", @@ -34,9 +52,8 @@ struct Command: ParsableCommand { ) fileprivate static func validateInput(_ options: ProcessingOptions) throws { - - guard FileManager.default.fileExists(atPath: options.configPath) else { - throw ValidationError("Options JSON file `\(options.configPath)` does not exist.") + if !FileManager.default.fileExists(atPath: (options.config as NSString).expandingTildeInPath) && URL(string: options.config) == nil { + throw ValidationError("Configuration is a nonexistent file or invalid request URL: \(options.config)") } } } @@ -54,7 +71,7 @@ extension Command { } mutating func run() throws { - try CodingOperation(options: options).execute() + try CodingOperation(options: options, credentials: try credentials).execute() } } } @@ -72,7 +89,7 @@ extension Command { } mutating func run() throws { - try CodingOperation(options: options).execute() + try CodingOperation(options: options, credentials: try credentials).execute() } } } diff --git a/Tests/MapboxDirectionsTests/MatchOptionsTests.swift b/Tests/MapboxDirectionsTests/MatchOptionsTests.swift index c5b30b7e9..7555cd099 100644 --- a/Tests/MapboxDirectionsTests/MatchOptionsTests.swift +++ b/Tests/MapboxDirectionsTests/MatchOptionsTests.swift @@ -26,7 +26,7 @@ class MatchOptionsTests: XCTestCase { XCTAssertEqual(unarchivedOptions.resamplesTraces, options.resamplesTraces) } - func testURLCoding() { + func testURLCoding() throws { let originalOptions = testMatchOptions originalOptions.resamplesTraces = true @@ -62,6 +62,10 @@ class MatchOptionsTests: XCTestCase { XCTAssertEqual(decodedOptions.profileIdentifier, originalOptions.profileIdentifier) XCTAssertEqual(decodedOptions.resamplesTraces, originalOptions.resamplesTraces) + + let matchURL = try XCTUnwrap(URL(string: "https://api.mapbox.com/matching/v5/mapbox/driving/-121.913565,37.331832;-121.916282,37.328707.json")) + XCTAssertNotNil(MatchOptions(url: matchURL)) + XCTAssertNil(RouteOptions(url: matchURL)) } // MARK: API name-handling tests diff --git a/Tests/MapboxDirectionsTests/RouteOptionsTests.swift b/Tests/MapboxDirectionsTests/RouteOptionsTests.swift index 3278e6a67..ca537682c 100644 --- a/Tests/MapboxDirectionsTests/RouteOptionsTests.swift +++ b/Tests/MapboxDirectionsTests/RouteOptionsTests.swift @@ -119,7 +119,7 @@ class RouteOptionsTests: XCTestCase { XCTAssertEqual(unarchivedOptions.maximumHeight, routeOptions.maximumHeight) } - func testURLCoding() { + func testURLCoding() throws { let originalOptions = testRouteOptions originalOptions.includesAlternativeRoutes = true @@ -191,6 +191,10 @@ class RouteOptionsTests: XCTestCase { XCTAssertNil(decodedOptions.arriveBy) // URL encoding skips seconds, so we check that dates are within 1 minute delta XCTAssertTrue(abs(decodedOptions.departAt!.timeIntervalSince(originalOptions.departAt!)) < 60) + + let routeURL = try XCTUnwrap(URL(string: "https://api.mapbox.com/directions/v5/mapbox/driving-traffic/-121.913565,37.331832;-121.916282,37.328707.json")) + XCTAssertNotNil(RouteOptions(url: routeURL)) + XCTAssertNil(MatchOptions(url: routeURL)) } // MARK: API name-handling tests