Skip to content

Commit

Permalink
Add methods for authors
Browse files Browse the repository at this point in the history
  • Loading branch information
rudrankriyam committed Oct 17, 2024
1 parent 3718e7b commit 52b49f7
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
69 changes: 69 additions & 0 deletions Sources/PhantomKit/GhostAuthor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation

/// Represents an author in the Ghost Content API.
///
/// Authors are a subset of users who have published posts associated with them.
public struct GhostAuthor: Codable, Sendable {
/// The unique identifier of the author.
public let id: String

/// The slug of the author, used in URLs.
public let slug: String

/// The name of the author.
public let name: String

/// The URL of the author's profile image.
public let profileImage: String?

/// The URL of the author's cover image.
public let coverImage: String?

/// The biography of the author.
public let bio: String?

/// The website URL of the author.
public let website: String?

/// The location of the author.
public let location: String?

/// The Facebook username of the author.
public let facebook: String?

/// The Twitter handle of the author.
public let twitter: String?

/// The meta title for SEO purposes.
public let metaTitle: String?

/// The meta description for SEO purposes.
public let metaDescription: String?

/// The full URL of the author's page on the Ghost site.
public let url: String

/// The number of posts associated with the author.
public let count: PostCount?

/// Represents the count of posts for an author.
public struct PostCount: Codable, Sendable {
/// The number of posts associated with the author.
public let posts: Int
}

enum CodingKeys: String, CodingKey {
case id, slug, name, bio, website, location, facebook, twitter, url
case profileImage = "profile_image"
case coverImage = "cover_image"
case metaTitle = "meta_title"
case metaDescription = "meta_description"
case count
}
}

/// Represents the response structure for author requests.
public struct GhostAuthorsResponse: Codable, Sendable {
/// An array of authors returned by the API.
public let authors: [GhostAuthor]
}
83 changes: 83 additions & 0 deletions Sources/PhantomKit/PhantomKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,89 @@ public actor PhantomKit {
}

/// Fetches tags from the Ghost Content API.

/// Fetches authors from the Ghost Content API.
///
/// This method retrieves authors who have published posts associated with them from the Ghost Content API.
///
/// - Parameters:
/// - limit: The maximum number of authors to return (default is 15).
/// - page: The page of authors to return (default is 1).
/// - include: Related data to include in the response (e.g., "count.posts").
///
/// - Returns: A `GhostAuthorsResponse` containing an array of `GhostAuthor` objects.
///
/// - Throws: An error if the network request fails, returns an invalid response, or fails to decode the JSON.
///
/// - Note: Authors that are not associated with a post are not returned.
public func getAuthors(
limit: Int = 15,
page: Int = 1,
include: String? = nil
) async throws -> GhostAuthorsResponse {
var parameters: [String: String] = [
"limit": String(limit),
"page": String(page)
]
if let include = include {
parameters["include"] = include
}
let data = try await get("authors", parameters: parameters)
let decoder = JSONDecoder()
return try decoder.decode(GhostAuthorsResponse.self, from: data)
}

/// Fetches a specific author by their ID from the Ghost Content API.
///
/// - Parameters:
/// - id: The unique identifier of the author.
/// - include: Related data to include in the response (e.g., "count.posts").
///
/// - Returns: A `GhostAuthor` object representing the requested author.
///
/// - Throws: An error if the network request fails, returns an invalid response, or fails to decode the JSON.
public func getAuthor(
id: String,
include: String? = nil
) async throws -> GhostAuthor {
var parameters: [String: String] = [:]
if let include = include {
parameters["include"] = include
}
let data = try await get("authors/\(id)", parameters: parameters)
let decoder = JSONDecoder()
let response = try decoder.decode(GhostAuthorsResponse.self, from: data)
guard let author = response.authors.first else {
throw NSError(domain: "PhantomKit", code: 0, userInfo: [NSLocalizedDescriptionKey: "Author not found"])
}
return author
}

/// Fetches a specific author by their slug from the Ghost Content API.
///
/// - Parameters:
/// - slug: The slug of the author.
/// - include: Related data to include in the response (e.g., "count.posts").
///
/// - Returns: A `GhostAuthor` object representing the requested author.
///
/// - Throws: An error if the network request fails, returns an invalid response, or fails to decode the JSON.
public func getAuthorBySlug(
slug: String,
include: String? = nil
) async throws -> GhostAuthor {
var parameters: [String: String] = [:]
if let include = include {
parameters["include"] = include
}
let data = try await get("authors/slug/\(slug)", parameters: parameters)
let decoder = JSONDecoder()
let response = try decoder.decode(GhostAuthorsResponse.self, from: data)
guard let author = response.authors.first else {
throw NSError(domain: "PhantomKit", code: 0, userInfo: [NSLocalizedDescriptionKey: "Author not found"])
}
return author
}
///
/// This method retrieves tags from the Ghost Content API. By default, it includes
/// internal tags. Use the `filter` parameter to limit the response to public tags.
Expand Down
38 changes: 38 additions & 0 deletions Tests/PhantomKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,42 @@ import Testing
#expect(page.featureImageAlt == nil, "Page feature image alt should be nil")
#expect(page.featureImageCaption == nil, "Page feature image caption should be nil")
}

/// Tests fetching a specific author by their ID from the Ghost Content API using PhantomKit.
///
/// This test case initializes a PhantomKit instance with the demo Ghost site's credentials
/// and attempts to fetch a specific author by their ID. It verifies that the API call succeeds
/// and returns the expected author data.
///
/// - Note: This test uses the public demo Ghost site (https://demo.ghost.io) and its Content API key.
/// The API key used here is for demonstration purposes only and may change in the future.
///
/// - Important: This test requires an active internet connection to succeed.
@Test("Fetch specific author by ID from Ghost Content API")
func fetchAuthorById() async throws {
// Arrange
let phantomKit = PhantomKit(
adminDomain: "demo.ghost.io",
apiKey: "22444f78447824223cefc48062"
)
let expectedAuthorId = "5979a779df093500228e9590"

// Act
let author = try await phantomKit.getAuthor(id: expectedAuthorId)

// Assert
#expect(author.id == expectedAuthorId, "Author ID should match the requested ID")
#expect(author.name == "Abraham Lincoln", "Author name should match expected value")
#expect(author.slug == "abe", "Author slug should match expected value")
#expect(author.profileImage == "https://demo.ghost.io/content/images/2018/10/abe.jpg", "Author profile image should match expected value")
#expect(author.coverImage == nil, "Author cover image should be nil")
#expect(author.bio == "I was the 16th president of the USA until I was assassinated in April of 1963. I led the US through its Civil War - its bloodiest and greatest moral, constitutional and political crisis.", "Author bio should match expected value")
#expect(author.website == nil, "Author website should be nil")
#expect(author.location == "Kentucky, USA", "Author location should match expected value")
#expect(author.facebook == nil, "Author Facebook should be nil")
#expect(author.twitter == nil, "Author Twitter should be nil")
#expect(author.metaTitle == nil, "Author meta title should be nil")
#expect(author.metaDescription == nil, "Author meta description should be nil")
#expect(author.url == "https://demo.ghost.io/author/abe/", "Author URL should match expected value")
}
}

0 comments on commit 52b49f7

Please sign in to comment.