Skip to content

Commit

Permalink
Merge pull request #1580 from apollographql/fix/file_vs_folder
Browse files Browse the repository at this point in the history
Add and test validation of multiple vs. single file urls
  • Loading branch information
designatednerd authored Dec 16, 2020
2 parents ffc3bb4 + 49905bc commit e9ff131
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 16 deletions.
15 changes: 15 additions & 0 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ public class ApolloCodegen {
/// Errors which can happen with code generation
public enum CodegenError: Error, LocalizedError {
case folderDoesNotExist(_ url: URL)
case multipleFilesButNotDirectoryURL(_ url: URL)
case singleFileButNotSwiftFileURL(_ url: URL)

public var errorDescription: String? {
switch self {
case .folderDoesNotExist(let url):
return "Can't run codegen trying to run the command from \(url) because there is no folder there! This should be the folder which, at some depth, contains all your `.graphql` files."
case .multipleFilesButNotDirectoryURL(let url):
return "Codegen is requesting multiple file generation, but the URL passed in (\(url)) is not a directory URL. Please check your URL and try again."
case .singleFileButNotSwiftFileURL(let url):
return "Codegen is requesting single file generation, but the URL passed in (\(url)) is a not a Swift file URL. Please check your URL and try again."
}
}
}
Expand All @@ -35,8 +41,17 @@ public class ApolloCodegen {

switch options.outputFormat {
case .multipleFiles(let folderURL):
/// We have to try to create the folder first because the stuff
/// underlying `isDirectoryURL` only works on actual directories
try FileManager.default.apollo.createFolderIfNeeded(at: folderURL)
guard folderURL.apollo.isDirectoryURL else {
throw CodegenError.multipleFilesButNotDirectoryURL(folderURL)
}
case .singleFile(let fileURL):
guard fileURL.apollo.isSwiftFileURL else {
throw CodegenError.singleFileButNotSwiftFileURL(fileURL)
}

try FileManager.default.apollo.createContainingFolderIfNeeded(for: fileURL)
}

Expand Down
44 changes: 29 additions & 15 deletions Sources/ApolloCodegenLib/FileFinder.swift
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import Foundation

public struct FileFinder {

#if compiler(>=5.3)
/// Version that works if you're using the 5.3 compiler or above
/// - Parameter filePath: The full file path of the file to find. Defaults to the `#filePath` of the caller.
/// - Returns: The file URL for the parent folder.
public static func findParentFolder(from filePath: StaticString = #filePath) -> URL {
self.findParentFolder(from: filePath.apollo.toString)
}
public static func findParentFolder(from filePath: StaticString = #filePath) -> URL {
self.findParentFolder(from: filePath.apollo.toString)
}

/// The URL of a file at a given path
/// - Parameter filePath: The full file path of the file to find
/// - Returns: The file's URL
public static func fileURL(from filePath: StaticString = #filePath) -> URL {
URL(fileURLWithPath: filePath.apollo.toString)
}
#else
/// Version that works if you're using the 5.2 compiler or below
/// - Parameter file: The full file path of the file to find. Defaults to the `#file` of the caller.
/// - Returns: The file URL for the parent folder.
public static func findParentFolder(from filePath: StaticString = #file) -> URL {
self.findParentFolder(from: filePath.apollo.toString)
}
/// Version that works if you're using the 5.2 compiler or below
/// - Parameter file: The full file path of the file to find. Defaults to the `#file` of the caller.
/// - Returns: The file URL for the parent folder.
public static func findParentFolder(from filePath: StaticString = #file) -> URL {
self.findParentFolder(from: filePath.apollo.toString)
}

/// The URL of a file at a given path
/// - Parameter filePath: The full file path of the file to find
/// - Returns: The file's URL
public static func fileURL(from filePath: StaticString = #file) -> URL {
URL(fileURLWithPath: filePath.apollo.toString)
}
#endif

/// Finds the parent folder from a given file path.
/// - Parameter filePath: The full file path, as a string
/// - Returns: The file URL for the parent folder.
public static func findParentFolder(from filePath: String) -> URL {
let url = URL(fileURLWithPath: filePath)
return url.deletingLastPathComponent()
}
public static func findParentFolder(from filePath: String) -> URL {
let url = URL(fileURLWithPath: filePath)
return url.deletingLastPathComponent()
}
}
19 changes: 19 additions & 0 deletions Sources/ApolloCodegenLib/URL+Apollo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ public enum ApolloURLError: Error, LocalizedError {

extension ApolloExtension where Base == URL {

/// Determines if the URL passed in is a directory URL.
///
/// NOTE: Only works if something at the URL already exists.
///
/// - Returns: True if the URL is a directory URL, false if it isn't.
var isDirectoryURL: Bool {
guard
let resourceValues = try? base.resourceValues(forKeys: [.isDirectoryKey]),
let isDirectory = resourceValues.isDirectory else {
return false
}

return isDirectory
}

var isSwiftFileURL: Bool {
base.pathExtension == "swift"
}

/// - Returns: the URL to the parent folder of the current URL.
public func parentFolderURL() -> URL {
base.deletingLastPathComponent()
Expand Down
48 changes: 48 additions & 0 deletions Tests/ApolloCodegenTests/ApolloCodegenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,54 @@ class ApolloCodegenTests: XCTestCase {
])
}

func testTryingToUseAFileURLToOutputMultipleFilesFails() {
let scriptFolderURL = CodegenTestHelper.cliFolderURL()
let starWarsFolderURL = CodegenTestHelper.starWarsFolderURL()
let starWarsSchemaFileURL = CodegenTestHelper.starWarsSchemaFileURL()
let outputFolder = CodegenTestHelper.outputFolderURL()
let outputFile = outputFolder.appendingPathComponent("API.swift")

let options = ApolloCodegenOptions(outputFormat: .multipleFiles(inFolderAtURL: outputFile),
urlToSchemaFile: starWarsSchemaFileURL,
downloadTimeout: CodegenTestHelper.timeout)
do {
_ = try ApolloCodegen.run(from: starWarsFolderURL,
with: scriptFolderURL,
options: options)
} catch {
switch error {
case ApolloCodegen.CodegenError.multipleFilesButNotDirectoryURL(let url):
XCTAssertEqual(url, outputFile)
default:
XCTFail("Unexpected error running codegen: \(error.localizedDescription)")

}
}
}

func testTryingToUseAFolderURLToOutputASingleFileFails() {
let scriptFolderURL = CodegenTestHelper.cliFolderURL()
let starWarsFolderURL = CodegenTestHelper.starWarsFolderURL()
let starWarsSchemaFileURL = CodegenTestHelper.starWarsSchemaFileURL()
let outputFolder = CodegenTestHelper.outputFolderURL()

let options = ApolloCodegenOptions(outputFormat: .singleFile(atFileURL: outputFolder),
urlToSchemaFile: starWarsSchemaFileURL,
downloadTimeout: CodegenTestHelper.timeout)
do {
_ = try ApolloCodegen.run(from: starWarsFolderURL,
with: scriptFolderURL,
options: options)
} catch {
switch error {
case ApolloCodegen.CodegenError.singleFileButNotSwiftFileURL(let url):
XCTAssertEqual(url, outputFolder)
default:
XCTFail("Unexpected error running codegen: \(error.localizedDescription)")
}
}
}

func testCodegenWithSingleFileOutputsSingleFile() throws {
let scriptFolderURL = CodegenTestHelper.cliFolderURL()
let starWarsFolderURL = CodegenTestHelper.starWarsFolderURL()
Expand Down
37 changes: 36 additions & 1 deletion Tests/ApolloCodegenTests/URLExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation
import XCTest
import ApolloCodegenLib
@testable import ApolloCodegenLib
import ApolloCore

class URLExtensionsTests: XCTestCase {
Expand Down Expand Up @@ -68,4 +68,39 @@ class URLExtensionsTests: XCTestCase {

XCTAssertEqual(child, expectedFile)
}

func testIsDirectoryForExistingDirectory() {
let parentDirectory = FileFinder.findParentFolder()
XCTAssertTrue(FileManager.default.apollo.folderExists(at: parentDirectory))
XCTAssertTrue(parentDirectory.apollo.isDirectoryURL)
}

func testIsDirectoryForExistingFile() {
let currentFileURL = FileFinder.fileURL()
XCTAssertTrue(FileManager.default.apollo.fileExists(at: currentFileURL))
XCTAssertFalse(currentFileURL.apollo.isDirectoryURL)
}

func testIsSwiftFileForExistingFile() {
let currentFileURL = FileFinder.fileURL()
XCTAssertTrue(FileManager.default.apollo.fileExists(at: currentFileURL))
XCTAssertTrue(currentFileURL.apollo.isSwiftFileURL)
}

func testIsSwiftFileForNonExistentFileWithSingleExtension() {
let currentDirectory = FileFinder.findParentFolder()
let doesntExist = currentDirectory.appendingPathComponent("test.swift")

XCTAssertFalse(FileManager.default.apollo.fileExists(at: doesntExist))
XCTAssertTrue(doesntExist.apollo.isSwiftFileURL)
}

func testIsSwiftFileForNonExistentFileWithMultipleExtensions() {
let currentDirectory = FileFinder.findParentFolder()
let doesntExist = currentDirectory.appendingPathComponent("test.graphql.swift")

XCTAssertFalse(FileManager.default.apollo.fileExists(at: doesntExist))
XCTAssertTrue(doesntExist.apollo.isSwiftFileURL)
}

}

0 comments on commit e9ff131

Please sign in to comment.