From 2aca6a1628b708d07ad321628d14d73591da1c14 Mon Sep 17 00:00:00 2001 From: Konstantinos Nikoloutsos Date: Thu, 11 Jul 2024 01:15:48 +0300 Subject: [PATCH 1/2] Add API implementation for repository contents --- OctoKit/Repositories.swift | 183 +++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/OctoKit/Repositories.swift b/OctoKit/Repositories.swift index 59dc7699..93e9c86a 100644 --- a/OctoKit/Repositories.swift +++ b/OctoKit/Repositories.swift @@ -70,6 +70,135 @@ open class Repository: Codable { } } +public struct Links: Codable { + public let git: String? + public let html: String? + public let selfLink: String + + private enum CodingKeys: String, CodingKey { + case git, html + case selfLink = "self" + } +} + +public struct ContentDirectoryItem: Codable { + public let type: String + public let size: Int + public let name: String + public let path: String + public let content: String? + public let sha: String + public let url: String + public let gitUrl: String? + public let htmlUrl: String? + public let downloadUrl: String? + public let links: Links + + private enum CodingKeys: String, CodingKey { + case type, size, name, path, content, sha, url + case gitUrl = "git_url" + case htmlUrl = "html_url" + case downloadUrl = "download_url" + case links = "_links" + } +} + +public struct ContentFile: Codable { + public let type: String + public let encoding: String + public let size: Int + public let name: String + public let path: String + public let content: String + public let sha: String + public let url: String + public let gitUrl: String? + public let htmlUrl: String? + public let downloadUrl: String? + public let links: Links + + private enum CodingKeys: String, CodingKey { + case type, encoding, size, name, path, content, sha, url + case gitUrl = "git_url" + case htmlUrl = "html_url" + case downloadUrl = "download_url" + case links = "_links" + } +} + +public struct SymlinkContent: Codable { + public let type: String + public let target: String + public let size: Int + public let name: String + public let path: String + public let sha: String + public let url: String + public let gitUrl: String? + public let htmlUrl: String? + public let downloadUrl: String? + public let links: Links + + private enum CodingKeys: String, CodingKey { + case type, target, size, name, path, sha, url + case gitUrl = "git_url" + case htmlUrl = "html_url" + case downloadUrl = "download_url" + case links = "_links" + } +} + +public struct SubmoduleContent: Codable { + public let type: String + public let submoduleGitUrl: String + public let size: Int + public let name: String + public let path: String + public let sha: String + public let url: String + public let gitUrl: String? + public let htmlUrl: String? + public let downloadUrl: String? + public let links: Links + + private enum CodingKeys: String, CodingKey { + case type, submoduleGitUrl = "submodule_git_url", size, name, path, sha, url + case gitUrl = "git_url" + case htmlUrl = "html_url" + case downloadUrl = "download_url" + case links = "_links" + } +} + +/// Response for decoding https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28 response +public enum ContentResponse: Codable { + case contentDirectory([ContentDirectoryItem]) + case contentFile(ContentFile) + case symlinkContent(SymlinkContent) + case submoduleContent(SubmoduleContent) + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let contentDirectoryItem = try? container.decode([ContentDirectoryItem].self) { + self = .contentDirectory(contentDirectoryItem) + return + } + if let contentFile = try? container.decode(ContentFile.self) { + self = .contentFile(contentFile) + return + } + if let symlinkContent = try? container.decode(SymlinkContent.self) { + self = .symlinkContent(symlinkContent) + return + } + if let submoduleContent = try? container.decode(SubmoduleContent.self) { + self = .submoduleContent(submoduleContent) + return + } + throw DecodingError.typeMismatch(ContentResponse.self, DecodingError.Context(codingPath: container.codingPath, debugDescription: "Unexpected type")) + } +} + // MARK: request public extension Octokit { @@ -149,6 +278,49 @@ public extension Octokit { return try await router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Repository.self) } #endif + + /** + Gets the contents of a file or directory in a repository. + [Github documentation](https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28) + - parameter owner: The account owner of the repository. The name is not case sensitive. + - parameter name: The name of the repository without the .git extension. The name is not case sensitive. + - parameter path: Specify the file path or directory with the path parameter. If you omit the path parameter, you will receive the contents of the repository's root directory. + - parameter ref: The name of the commit/branch/tag. Default: the repository’s default branch. + - parameter completion: Callback for the outcome of the fetch. Depending on the provided path a different enum value may be returned. + */ + @discardableResult + func repositoryContent(owner: String, + name: String, + path: String?, + ref: String?, + completion: @escaping (_ response: Result) -> Void) -> URLSessionDataTaskProtocol? { + let router = RepositoryRouter.getRepositoryContent(configuration, owner, name, path, ref) + return router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: ContentResponse.self) { contentResponse, error in + if let error = error { + completion(.failure(error)) + } else { + if let contentResponse = contentResponse { + completion(.success(contentResponse)) + } + } + } + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + /** + Gets the contents of a file or directory in a repository. + [Github documentation](https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28) + - parameter owner: The account owner of the repository. The name is not case sensitive. + - parameter name: The name of the repository without the .git extension. The name is not case sensitive. + - parameter path: Specify the file path or directory with the path parameter. If you omit the path parameter, you will receive the contents of the repository's root directory. + - parameter ref: The name of the commit/branch/tag. Default: the repository’s default branch. + */ + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func repositoryContent(owner: String, name: String, path: String?, ref: String? = nil) async throws -> ContentResponse { + let router = RepositoryRouter.getRepositoryContent(configuration, owner, name, path, ref) + return try await router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: ContentResponse.self) + } + #endif } // MARK: Router @@ -157,12 +329,14 @@ enum RepositoryRouter: Router { case readRepositories(Configuration, String, String, String) case readAuthenticatedRepositories(Configuration, String, String) case readRepository(Configuration, String, String) + case getRepositoryContent(Configuration, String, String, String?, String?) var configuration: Configuration { switch self { case let .readRepositories(config, _, _, _): return config case let .readAuthenticatedRepositories(config, _, _): return config case let .readRepository(config, _, _): return config + case let .getRepositoryContent(config, _, _, _, _): return config } } @@ -182,6 +356,11 @@ enum RepositoryRouter: Router { return ["per_page": perPage, "page": page] case .readRepository: return [:] + case let .getRepositoryContent(_, _, _, _, ref): + if let ref = ref { + return ["ref": ref] + } + return [:] } } @@ -193,6 +372,10 @@ enum RepositoryRouter: Router { return "user/repos" case let .readRepository(_, owner, name): return "repos/\(owner)/\(name)" + case let .getRepositoryContent(_, owner, repo, searchPath, _): + var path = "repos/\(owner)/\(repo)/contents" + if let searchPath = searchPath { path.append("/\(searchPath)") } + return path } } } From cb017122ca43da79ae9d723047f8953893b037b2 Mon Sep 17 00:00:00 2001 From: Konstantinos Nikoloutsos Date: Thu, 11 Jul 2024 01:16:29 +0300 Subject: [PATCH 2/2] Add unit tests for Repository Contents --- Tests/OctoKitTests/Fixtures/content.json | 18 + .../Fixtures/content_submodule.json | 17 + Tests/OctoKitTests/Fixtures/contents.json | 338 ++++++++++++++++++ Tests/OctoKitTests/RepositoryTests.swift | 210 +++++++++++ 4 files changed, 583 insertions(+) create mode 100644 Tests/OctoKitTests/Fixtures/content.json create mode 100644 Tests/OctoKitTests/Fixtures/content_submodule.json create mode 100644 Tests/OctoKitTests/Fixtures/contents.json diff --git a/Tests/OctoKitTests/Fixtures/content.json b/Tests/OctoKitTests/Fixtures/content.json new file mode 100644 index 00000000..92521f3a --- /dev/null +++ b/Tests/OctoKitTests/Fixtures/content.json @@ -0,0 +1,18 @@ +{ + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main" + }, + "content": "Ly8gc3dpZnQtdG9vbHMtdmVyc2lvbjo0LjAKLy8gVGhlIHN3aWZ0LXRvb2xz\nLXZlcnNpb24gZGVjbGFyZXMgdGhlIG1pbmltdW0gdmVyc2lvbiBvZiBTd2lm\ndCByZXF1aXJlZCB0byBidWlsZCB0aGlzIHBhY2thZ2UuCgppbXBvcnQgUGFj\na2FnZURlc2NyaXB0aW9uCgpsZXQgcGFja2FnZSA9IFBhY2thZ2UoCiAgICBu\nYW1lOiAiT2N0b0tpdCIsCgogICAgcHJvZHVjdHM6IFsKICAgICAgICAubGli\ncmFyeSgKICAgICAgICAgICAgbmFtZTogIk9jdG9LaXQiLAogICAgICAgICAg\nICB0YXJnZXRzOiBbIk9jdG9LaXQiXQogICAgICAgICksCiAgICBdLAogICAg\nZGVwZW5kZW5jaWVzOiBbCiAgICAgICAgLnBhY2thZ2UodXJsOiAiaHR0cHM6\nLy9naXRodWIuY29tL25lcmRpc2hieW5hdHVyZS9SZXF1ZXN0S2l0LmdpdCIs\nIGZyb206ICIzLjMuMCIpLAogICAgICAgIC5wYWNrYWdlKHVybDogImh0dHBz\nOi8vZ2l0aHViLmNvbS9uaWNrbG9ja3dvb2QvU3dpZnRGb3JtYXQiLCBmcm9t\nOiAiMC41Mi44IikKICAgIF0sCiAgICB0YXJnZXRzOiBbCiAgICAgICAgLnRh\ncmdldCgKICAgICAgICAgICAgbmFtZTogIk9jdG9LaXQiLAogICAgICAgICAg\nICBkZXBlbmRlbmNpZXM6IFsiUmVxdWVzdEtpdCJdLAogICAgICAgICAgICBw\nYXRoOiAiT2N0b0tpdCIKICAgICAgICApLAogICAgICAgIC50ZXN0VGFyZ2V0\nKAogICAgICAgICAgICBuYW1lOiAiT2N0b0tpdFRlc3RzIiwKICAgICAgICAg\nICAgZGVwZW5kZW5jaWVzOiBbIk9jdG9LaXQiXQogICAgICAgICksCiAgICBd\nCikK\n", + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Package.swift", + "encoding": "base64", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift", + "name": "Package.swift", + "path": "Package.swift", + "sha": "7ff4c2ce2bce119effa766ba6845200f59e17414", + "size": 768, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main" +} diff --git a/Tests/OctoKitTests/Fixtures/content_submodule.json b/Tests/OctoKitTests/Fixtures/content_submodule.json new file mode 100644 index 00000000..a7ebaab4 --- /dev/null +++ b/Tests/OctoKitTests/Fixtures/content_submodule.json @@ -0,0 +1,17 @@ +{ + "_links": { + "git": "https://api.github.com/repos/muter-mutation-testing/homebrew-formulae/git/trees/4e30ce4a9b6137502cf131664d412000bdd93007", + "html": "https://github.com/muter-mutation-testing/homebrew-formulae/tree/4e30ce4a9b6137502cf131664d412000bdd93007", + "self": "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/muter-mutation-testing/homebrew-formulae/git/trees/4e30ce4a9b6137502cf131664d412000bdd93007", + "html_url": "https://github.com/muter-mutation-testing/homebrew-formulae/tree/4e30ce4a9b6137502cf131664d412000bdd93007", + "name": "homebrew-formulae", + "path": "homebrew-formulae", + "sha": "4e30ce4a9b6137502cf131664d412000bdd93007", + "size": 0, + "submodule_git_url": "https://github.com/muter-mutation-testing/homebrew-formulae.git", + "type": "submodule", + "url": "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master" +} diff --git a/Tests/OctoKitTests/Fixtures/contents.json b/Tests/OctoKitTests/Fixtures/contents.json new file mode 100644 index 00000000..a53bf488 --- /dev/null +++ b/Tests/OctoKitTests/Fixtures/contents.json @@ -0,0 +1,338 @@ +[ + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/34da4e28f5ca7e3c2629cf8a213bba909b4715fe", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/.devcontainer", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.devcontainer?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/34da4e28f5ca7e3c2629cf8a213bba909b4715fe", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/.devcontainer", + "name": ".devcontainer", + "path": ".devcontainer", + "sha": "34da4e28f5ca7e3c2629cf8a213bba909b4715fe", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.devcontainer?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/1ba8d8461399a736097ba490c96b10695fab1a4c", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/.github", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.github?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/1ba8d8461399a736097ba490c96b10695fab1a4c", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/.github", + "name": ".github", + "path": ".github", + "sha": "1ba8d8461399a736097ba490c96b10695fab1a4c", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.github?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/a1b729f00aac01cbdf8a510ddfca6fd910970621", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/.gitignore", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.gitignore?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/.gitignore", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/a1b729f00aac01cbdf8a510ddfca6fd910970621", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/.gitignore", + "name": ".gitignore", + "path": ".gitignore", + "sha": "a1b729f00aac01cbdf8a510ddfca6fd910970621", + "size": 357, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.gitignore?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/819e07a22435f1e8efcbdd1d1c062deef0e501b1", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/.swift-version", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.swift-version?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/.swift-version", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/819e07a22435f1e8efcbdd1d1c062deef0e501b1", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/.swift-version", + "name": ".swift-version", + "path": ".swift-version", + "sha": "819e07a22435f1e8efcbdd1d1c062deef0e501b1", + "size": 4, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.swift-version?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/da2af0b468fc0f0bd92beeae3dc937d7c764dbfd", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/.swiftformat", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.swiftformat?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/.swiftformat", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/da2af0b468fc0f0bd92beeae3dc937d7c764dbfd", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/.swiftformat", + "name": ".swiftformat", + "path": ".swiftformat", + "sha": "da2af0b468fc0f0bd92beeae3dc937d7c764dbfd", + "size": 396, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/.swiftformat?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7fa463fe61b8db3268603bfe0b6d4a3847ae50ca", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Cartfile", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Cartfile?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Cartfile", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7fa463fe61b8db3268603bfe0b6d4a3847ae50ca", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Cartfile", + "name": "Cartfile", + "path": "Cartfile", + "sha": "7fa463fe61b8db3268603bfe0b6d4a3847ae50ca", + "size": 45, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Cartfile?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/1593817d8f3f8d4aa4425a786d078461c059e928", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Cartfile.resolved", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Cartfile.resolved?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Cartfile.resolved", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/1593817d8f3f8d4aa4425a786d078461c059e928", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Cartfile.resolved", + "name": "Cartfile.resolved", + "path": "Cartfile.resolved", + "sha": "1593817d8f3f8d4aa4425a786d078461c059e928", + "size": 44, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Cartfile.resolved?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/82d1e3049418f0ccaee2eaa282043c8598877625", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Gemfile", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Gemfile?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Gemfile", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/82d1e3049418f0ccaee2eaa282043c8598877625", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Gemfile", + "name": "Gemfile", + "path": "Gemfile", + "sha": "82d1e3049418f0ccaee2eaa282043c8598877625", + "size": 62, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Gemfile?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/d5d355293b4112270cfc2943be468e7419ec781a", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Gemfile.lock", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Gemfile.lock?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Gemfile.lock", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/d5d355293b4112270cfc2943be468e7419ec781a", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Gemfile.lock", + "name": "Gemfile.lock", + "path": "Gemfile.lock", + "sha": "d5d355293b4112270cfc2943be468e7419ec781a", + "size": 7730, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Gemfile.lock?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/83088aacb9ca3ab5e9f93dff90082a10cd888baf", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/LICENSE", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/LICENSE?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/LICENSE", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/83088aacb9ca3ab5e9f93dff90082a10cd888baf", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/LICENSE", + "name": "LICENSE", + "path": "LICENSE", + "sha": "83088aacb9ca3ab5e9f93dff90082a10cd888baf", + "size": 1055, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/LICENSE?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/d8c000a4eadf605a436cf6a4a9fee4005ef4c4cd", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Makefile", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Makefile?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Makefile", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/d8c000a4eadf605a436cf6a4a9fee4005ef4c4cd", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Makefile", + "name": "Makefile", + "path": "Makefile", + "sha": "d8c000a4eadf605a436cf6a4a9fee4005ef4c4cd", + "size": 616, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Makefile?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/9c24720095993ed3eadbc35dc4f35a6f29044e10", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/OctoKit.swift.podspec", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit.swift.podspec?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/OctoKit.swift.podspec", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/9c24720095993ed3eadbc35dc4f35a6f29044e10", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/OctoKit.swift.podspec", + "name": "OctoKit.swift.podspec", + "path": "OctoKit.swift.podspec", + "sha": "9c24720095993ed3eadbc35dc4f35a6f29044e10", + "size": 1065, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit.swift.podspec?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/ad7755047be3c220294e9ac0777ed35c99378cb2", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKit.xcodeproj", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit.xcodeproj?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/ad7755047be3c220294e9ac0777ed35c99378cb2", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKit.xcodeproj", + "name": "OctoKit.xcodeproj", + "path": "OctoKit.xcodeproj", + "sha": "ad7755047be3c220294e9ac0777ed35c99378cb2", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit.xcodeproj?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/bc6f8808288cba0c3975f26a61f3685f5f58de2e", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKit", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/bc6f8808288cba0c3975f26a61f3685f5f58de2e", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKit", + "name": "OctoKit", + "path": "OctoKit", + "sha": "bc6f8808288cba0c3975f26a61f3685f5f58de2e", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKit?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/c634925d9cc3a4c441a4fce6272da8f759e1eaca", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKitCLI", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKitCLI?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/c634925d9cc3a4c441a4fce6272da8f759e1eaca", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/OctoKitCLI", + "name": "OctoKitCLI", + "path": "OctoKitCLI", + "sha": "c634925d9cc3a4c441a4fce6272da8f759e1eaca", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/OctoKitCLI?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/95a48fe371df1a80193fec3b460eb596485ce516", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.resolved", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.resolved?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Package.resolved", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/95a48fe371df1a80193fec3b460eb596485ce516", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.resolved", + "name": "Package.resolved", + "path": "Package.resolved", + "sha": "95a48fe371df1a80193fec3b460eb596485ce516", + "size": 609, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.resolved?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Package.swift", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift", + "name": "Package.swift", + "path": "Package.swift", + "sha": "7ff4c2ce2bce119effa766ba6845200f59e17414", + "size": 768, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/cde3a932e6311d1f24370c09e1e720e091236040", + "html": "https://github.com/nerdishbynature/octokit.swift/blob/main/README.md", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/README.md?ref=main" + }, + "download_url": "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/README.md", + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/cde3a932e6311d1f24370c09e1e720e091236040", + "html_url": "https://github.com/nerdishbynature/octokit.swift/blob/main/README.md", + "name": "README.md", + "path": "README.md", + "sha": "cde3a932e6311d1f24370c09e1e720e091236040", + "size": 9611, + "type": "file", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/README.md?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/602bcffb4f2c3a5154c439daf380d65bdc9918fc", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/Script", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Script?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/602bcffb4f2c3a5154c439daf380d65bdc9918fc", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/Script", + "name": "Script", + "path": "Script", + "sha": "602bcffb4f2c3a5154c439daf380d65bdc9918fc", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Script?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/d3b78fe1411e62e9b694a0141cc5eb6af38e73c2", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/Tests", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Tests?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/d3b78fe1411e62e9b694a0141cc5eb6af38e73c2", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/Tests", + "name": "Tests", + "path": "Tests", + "sha": "d3b78fe1411e62e9b694a0141cc5eb6af38e73c2", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Tests?ref=main" + }, + { + "_links": { + "git": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/84f1731af7b7dbb0835238b722c1dcf2f62e6d1e", + "html": "https://github.com/nerdishbynature/octokit.swift/tree/main/fastlane", + "self": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/fastlane?ref=main" + }, + "download_url": null, + "git_url": "https://api.github.com/repos/nerdishbynature/octokit.swift/git/trees/84f1731af7b7dbb0835238b722c1dcf2f62e6d1e", + "html_url": "https://github.com/nerdishbynature/octokit.swift/tree/main/fastlane", + "name": "fastlane", + "path": "fastlane", + "sha": "84f1731af7b7dbb0835238b722c1dcf2f62e6d1e", + "size": 0, + "type": "dir", + "url": "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/fastlane?ref=main" + } +] diff --git a/Tests/OctoKitTests/RepositoryTests.swift b/Tests/OctoKitTests/RepositoryTests.swift index a8d924d4..e95a12ad 100644 --- a/Tests/OctoKitTests/RepositoryTests.swift +++ b/Tests/OctoKitTests/RepositoryTests.swift @@ -172,6 +172,150 @@ class RepositoryTests: XCTestCase { } #endif + func testfailureRepositoryContent() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift", + expectedHTTPMethod: "GET", + jsonFile: "content", + statusCode: 404) + + Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: "Package.swift", + ref: nil) { response in + switch response { + case .success: + XCTFail("repositoryContent() call should have returned an error response.") + case let .failure(error as NSError): + XCTAssertEqual(error.code, 404) + XCTAssertEqual(error.domain, OctoKitErrorDomain) + } + } + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testfailureRepositoryContentAsync() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift", + expectedHTTPMethod: "GET", + jsonFile: "content", + statusCode: 404) + + do { + _ = try await Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: "Package.swift") + XCTFail("repositoryContent function should have thrown an error") + } catch { + XCTAssertTrue(session.wasCalled) + } + } + #endif + + func testRepositoryContentWithContentFileResponse() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift", + expectedHTTPMethod: "GET", + jsonFile: "content", + statusCode: 200) + + Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: "Package.swift", + ref: nil) { response in + switch response { + case .success: + break + case .failure: + XCTFail("repositoryContent() call should not have returned an error response.") + } + } + XCTAssertTrue(session.wasCalled) + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testRepositoryContentAsyncWithContentFileResponse() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift", + expectedHTTPMethod: "GET", + jsonFile: "content", + statusCode: 200) + _ = try await Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: "Package.swift") + XCTAssertTrue(session.wasCalled) + } + #endif + + func testRepositoryContentWithDirResponse() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents", + expectedHTTPMethod: "GET", + jsonFile: "contents", + statusCode: 200) + Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: nil, + ref: nil) { response in + switch response { + case let .success(contentResponse): + guard case .contentDirectory = contentResponse else { + XCTFail("ContentResponse enum should have returned contentDirectory case") + return + } + case .failure: + XCTFail("repositoryContent() call should not have returned an error response.") + } + } + XCTAssertTrue(session.wasCalled) + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testRepositoryContentAsyncWithDirResponse() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/nerdishbynature/octokit.swift/contents", + expectedHTTPMethod: "GET", + jsonFile: "contents", + statusCode: 200) + _ = try await Octokit(session: session).repositoryContent(owner: "nerdishbynature", + name: "octokit.swift", + path: nil, + ref: nil) + XCTAssertTrue(session.wasCalled) + } + #endif + + func testRepositoryContentWithSubmoduleResponse() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master", + expectedHTTPMethod: "GET", + jsonFile: "content_submodule", + statusCode: 200) + Octokit(session: session).repositoryContent(owner: "muter-mutation-testing", + name: "muter", + path: "homebrew-formulae", + ref: "master") { response in + switch response { + case .success: + break + case .failure: + XCTFail("repositoryContent() call should not have returned an error response.") + } + } + XCTAssertTrue(session.wasCalled) + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testRepositoryContentAsyncWithSubmoduleResponse() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master", + expectedHTTPMethod: "GET", + jsonFile: "content_submodule", + statusCode: 200) + _ = try await Octokit(session: session).repositoryContent(owner: "muter-mutation-testing", + name: "muter", + path: "homebrew-formulae", + ref: "master") + XCTAssertTrue(session.wasCalled) + } + #endif + // MARK: Model Tests func testUserParsingFullRepository() { @@ -190,4 +334,70 @@ class RepositoryTests: XCTestCase { XCTAssertEqual(subject.cloneURL, "https://github.com/mietzmithut/Test.git") XCTAssertEqual(subject.size, 132) } + + func testContentFileParsing() { + let subject = Helper.codableFromFile("content", type: ContentResponse.self) + guard case let .contentFile(content) = subject else { return } + + XCTAssertEqual(content.url, "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main") + XCTAssertEqual(content.type, "file") + XCTAssertEqual(content.size, 768) + XCTAssertEqual(content.downloadUrl, "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Package.swift") + XCTAssertEqual(content.sha, "7ff4c2ce2bce119effa766ba6845200f59e17414") + XCTAssertEqual(content.path, "Package.swift") + XCTAssertEqual(content.htmlUrl, "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift") + XCTAssertEqual(content.gitUrl, "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414") + XCTAssertEqual(content.encoding, "base64") + XCTAssertEqual(content.downloadUrl, "https://raw.githubusercontent.com/nerdishbynature/octokit.swift/main/Package.swift") + XCTAssertEqual(content.content, + "Ly8gc3dpZnQtdG9vbHMtdmVyc2lvbjo0LjAKLy8gVGhlIHN3aWZ0LXRvb2xz\nLXZlcnNpb24gZGVjbGFyZXMgdGhlIG1pbmltdW0gdmVyc2lvbiBvZiBTd2lm\ndCByZXF1aXJlZCB0byBidWlsZCB0aGlzIHBhY2thZ2UuCgppbXBvcnQgUGFj\na2FnZURlc2NyaXB0aW9uCgpsZXQgcGFja2FnZSA9IFBhY2thZ2UoCiAgICBu\nYW1lOiAiT2N0b0tpdCIsCgogICAgcHJvZHVjdHM6IFsKICAgICAgICAubGli\ncmFyeSgKICAgICAgICAgICAgbmFtZTogIk9jdG9LaXQiLAogICAgICAgICAg\nICB0YXJnZXRzOiBbIk9jdG9LaXQiXQogICAgICAgICksCiAgICBdLAogICAg\nZGVwZW5kZW5jaWVzOiBbCiAgICAgICAgLnBhY2thZ2UodXJsOiAiaHR0cHM6\nLy9naXRodWIuY29tL25lcmRpc2hieW5hdHVyZS9SZXF1ZXN0S2l0LmdpdCIs\nIGZyb206ICIzLjMuMCIpLAogICAgICAgIC5wYWNrYWdlKHVybDogImh0dHBz\nOi8vZ2l0aHViLmNvbS9uaWNrbG9ja3dvb2QvU3dpZnRGb3JtYXQiLCBmcm9t\nOiAiMC41Mi44IikKICAgIF0sCiAgICB0YXJnZXRzOiBbCiAgICAgICAgLnRh\ncmdldCgKICAgICAgICAgICAgbmFtZTogIk9jdG9LaXQiLAogICAgICAgICAg\nICBkZXBlbmRlbmNpZXM6IFsiUmVxdWVzdEtpdCJdLAogICAgICAgICAgICBw\nYXRoOiAiT2N0b0tpdCIKICAgICAgICApLAogICAgICAgIC50ZXN0VGFyZ2V0\nKAogICAgICAgICAgICBuYW1lOiAiT2N0b0tpdFRlc3RzIiwKICAgICAgICAg\nICAgZGVwZW5kZW5jaWVzOiBbIk9jdG9LaXQiXQogICAgICAgICksCiAgICBd\nCikK\n") + XCTAssertEqual(content.links.git, "https://api.github.com/repos/nerdishbynature/octokit.swift/git/blobs/7ff4c2ce2bce119effa766ba6845200f59e17414") + XCTAssertEqual(content.links.html, "https://github.com/nerdishbynature/octokit.swift/blob/main/Package.swift") + XCTAssertEqual(content.links.selfLink, "https://api.github.com/repos/nerdishbynature/octokit.swift/contents/Package.swift?ref=main") + } + + func testContentDirectoryParsing() { + let subject = Helper.codableFromFile("contents", type: ContentResponse.self) + guard case let .contentDirectory(contentItems) = subject else { return } + XCTAssertEqual(contentItems.count, 21) + XCTAssertEqual(contentItems[0].name, ".devcontainer") + XCTAssertEqual(contentItems[1].name, ".github") + XCTAssertEqual(contentItems[2].name, ".gitignore") + XCTAssertEqual(contentItems[3].name, ".swift-version") + XCTAssertEqual(contentItems[4].name, ".swiftformat") + XCTAssertEqual(contentItems[5].name, "Cartfile") + XCTAssertEqual(contentItems[6].name, "Cartfile.resolved") + XCTAssertEqual(contentItems[7].name, "Gemfile") + XCTAssertEqual(contentItems[8].name, "Gemfile.lock") + XCTAssertEqual(contentItems[9].name, "LICENSE") + XCTAssertEqual(contentItems[10].name, "Makefile") + XCTAssertEqual(contentItems[11].name, "OctoKit.swift.podspec") + XCTAssertEqual(contentItems[12].name, "OctoKit.xcodeproj") + XCTAssertEqual(contentItems[13].name, "OctoKit") + XCTAssertEqual(contentItems[14].name, "OctoKitCLI") + XCTAssertEqual(contentItems[15].name, "Package.resolved") + XCTAssertEqual(contentItems[16].name, "Package.swift") + XCTAssertEqual(contentItems[17].name, "README.md") + XCTAssertEqual(contentItems[18].name, "Script") + XCTAssertEqual(contentItems[19].name, "Tests") + XCTAssertEqual(contentItems[20].name, "fastlane") + } + + func testSubmoduleContentParsing() { + let subject = Helper.codableFromFile("content_submodule", type: ContentResponse.self) + guard case let .submoduleContent(submoduleContent) = subject else { return } + XCTAssertEqual(submoduleContent.url, "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master") + XCTAssertEqual(submoduleContent.type, "submodule") + XCTAssertEqual(submoduleContent.submoduleGitUrl, "https://github.com/muter-mutation-testing/homebrew-formulae.git") + XCTAssertEqual(submoduleContent.size, 0) + XCTAssertEqual(submoduleContent.sha, "4e30ce4a9b6137502cf131664d412000bdd93007") + XCTAssertEqual(submoduleContent.path, "homebrew-formulae") + XCTAssertEqual(submoduleContent.name, "homebrew-formulae") + XCTAssertEqual(submoduleContent.htmlUrl, "https://github.com/muter-mutation-testing/homebrew-formulae/tree/4e30ce4a9b6137502cf131664d412000bdd93007") + XCTAssertEqual(submoduleContent.gitUrl, "https://api.github.com/repos/muter-mutation-testing/homebrew-formulae/git/trees/4e30ce4a9b6137502cf131664d412000bdd93007") + XCTAssertEqual(submoduleContent.downloadUrl, nil) + XCTAssertEqual(submoduleContent.links.git, "https://api.github.com/repos/muter-mutation-testing/homebrew-formulae/git/trees/4e30ce4a9b6137502cf131664d412000bdd93007") + XCTAssertEqual(submoduleContent.links.html, "https://github.com/muter-mutation-testing/homebrew-formulae/tree/4e30ce4a9b6137502cf131664d412000bdd93007") + XCTAssertEqual(submoduleContent.links.selfLink, "https://api.github.com/repos/muter-mutation-testing/muter/contents/homebrew-formulae?ref=master") + } }