Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Person external IDs #132

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Sources/TMDb/Models/MovieExternalLinksCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ public struct MovieExternalLinksCollection: Identifiable, Codable, Equatable, Ha

public static func == (lhs: MovieExternalLinksCollection, rhs: MovieExternalLinksCollection) -> Bool {
lhs.id == rhs.id
&& lhs.imdb?.id == rhs.imdb?.id
&& lhs.wikiData?.id == rhs.wikiData?.id
&& lhs.facebook?.id == rhs.facebook?.id
&& lhs.instagram?.id == rhs.instagram?.id
&& lhs.twitter?.id == rhs.twitter?.id
&& lhs.imdb == rhs.imdb
&& lhs.wikiData == rhs.wikiData
&& lhs.facebook == rhs.facebook
&& lhs.instagram == rhs.instagram
&& lhs.twitter == rhs.twitter
}

}
Expand Down
149 changes: 149 additions & 0 deletions Sources/TMDb/Models/PersonExternalLinksCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import Foundation

///
/// A model representing a collection of media databases and social IDs and links for a person.
///
public struct PersonExternalLinksCollection: Identifiable, Codable, Equatable, Hashable {

///
/// The TMDb person identifier.
///
public let id: Person.ID

///
/// IMDb link.
///
public let imdb: IMDbLink?

///
/// WikiData link.
///
public let wikiData: WikiDataLink?

///
/// Facebook link.
///
public let facebook: FacebookLink?

///
/// Instagram link.
///
public let instagram: InstagramLink?

///
/// Twitter link.
///
public let twitter: TwitterLink?

///
/// TikTok llink.
///
public let tikTok: TikTokLink?

///
/// Creates an external links collection for a movie.
///
/// - Parameters:
/// - id: The TMDb person identifier.
/// - imdb: IMDb link.
/// - wikiData: WikiData link.
/// - facebook: Facebook link.
/// - instagram: Instagram link.
/// - twitter: Twitter link.
/// - tikTok: TikTok link.
///
public init(
id: Movie.ID,
imdb: IMDbLink? = nil,
wikiData: WikiDataLink? = nil,
facebook: FacebookLink? = nil,
instagram: InstagramLink? = nil,
twitter: TwitterLink? = nil,
tikTok: TikTokLink? = nil
) {
self.id = id
self.imdb = imdb
self.wikiData = wikiData
self.facebook = facebook
self.instagram = instagram
self.twitter = twitter
self.tikTok = tikTok
}

public func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(imdb?.id)
hasher.combine(wikiData?.id)
hasher.combine(facebook?.id)
hasher.combine(instagram?.id)
hasher.combine(twitter?.id)
hasher.combine(tikTok?.id)
}

public static func == (lhs: PersonExternalLinksCollection, rhs: PersonExternalLinksCollection) -> Bool {
lhs.id == rhs.id
&& lhs.imdb == rhs.imdb
&& lhs.wikiData == rhs.wikiData
&& lhs.facebook == rhs.facebook
&& lhs.instagram == rhs.instagram
&& lhs.twitter == rhs.twitter
&& lhs.tikTok == rhs.tikTok
}

}

extension PersonExternalLinksCollection {

private enum CodingKeys: String, CodingKey {
case id
case imdbID = "imdbId"
case wikiDataID = "wikidataId"
case facebookID = "facebookId"
case instagramID = "instagramId"
case twitterID = "twitterId"
case tikTokID = "tiktokId"
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let id = try container.decode(Movie.ID.self, forKey: .id)

let imdbID = try container.decodeIfPresent(String.self, forKey: .imdbID)
let wikiDataID = try container.decodeIfPresent(String.self, forKey: .wikiDataID)
let facebookID = try container.decodeIfPresent(String.self, forKey: .facebookID)
let instagramID = try container.decodeIfPresent(String.self, forKey: .instagramID)
let twitterID = try container.decodeIfPresent(String.self, forKey: .twitterID)
let tikTokID = try container.decodeIfPresent(String.self, forKey: .tikTokID)

let imdbLink = IMDbLink(imdbNameID: imdbID)
let wikiDataLink = WikiDataLink(wikiDataID: wikiDataID)
let facebookLink = FacebookLink(facebookID: facebookID)
let instagramLink = InstagramLink(instagramID: instagramID)
let twitterLink = TwitterLink(twitterID: twitterID)
let tikTokLink = TikTokLink(tikTokID: tikTokID)

self.init(
id: id,
imdb: imdbLink,
wikiData: wikiDataLink,
facebook: facebookLink,
instagram: instagramLink,
twitter: twitterLink,
tikTok: tikTokLink
)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(id, forKey: .id)
try container.encodeIfPresent(imdb?.id, forKey: .imdbID)
try container.encodeIfPresent(wikiData?.id, forKey: .wikiDataID)
try container.encodeIfPresent(facebook?.id, forKey: .facebookID)
try container.encodeIfPresent(instagram?.id, forKey: .instagramID)
try container.encodeIfPresent(twitter?.id, forKey: .twitterID)
try container.encodeIfPresent(tikTok?.id, forKey: .tikTokID)
}

}
53 changes: 53 additions & 0 deletions Sources/TMDb/Models/TikTokLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation

///
/// A TikTok external link.
///
/// e.g. to a person's TikTok profile.
///
public struct TikTokLink: ExternalLink {

///
/// TikTok profile identifier.
///
public let id: String

///
/// URL of the TikTok profile page.
///
public let url: URL

///
/// Creates a TikTok link object using a TikTok user identifier.
///
/// - Parameter tikTokID: The TikTok user identifier.
///
public init?(tikTokID: String?) {
guard
let tikTokID,
let url = Self.tikTokURL(for: tikTokID)
else {
return nil
}

self.init(id: tikTokID, url: url)
}

}

extension TikTokLink {

private init(id: String, url: URL) {
self.id = id
self.url = url
}

}

extension TikTokLink {

private static func tikTokURL(for tikTokID: String) -> URL? {
URL(string: "https://www.tiktok.com/@\(tikTokID)")
}

}
6 changes: 3 additions & 3 deletions Sources/TMDb/Movies/MovieService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,14 +331,14 @@ public final class MovieService {
/// Returns a collection of media databases and social links for a movie.
///
/// - Parameters:
/// - id: The identifier of the movie.
/// - movieID: The identifier of the movie.
///
/// - Returns: A collection of external links for the specificed movie.
///
public func externalLinks(forMovie id: Movie.ID) async throws -> MovieExternalLinksCollection {
public func externalLinks(forMovie movieID: Movie.ID) async throws -> MovieExternalLinksCollection {
let linksCollection: MovieExternalLinksCollection
do {
linksCollection = try await apiClient.get(endpoint: MoviesEndpoint.externalIDs(movieID: id))
linksCollection = try await apiClient.get(endpoint: MoviesEndpoint.externalIDs(movieID: movieID))
} catch let error {
throw TMDbError(error: error)
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/TMDb/People/Endpoints/PeopleEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum PeopleEndpoint {
case tvSeriesCredits(personID: Person.ID)
case images(personID: Person.ID)
case popular(page: Int? = nil)
case externalIDs(personID: Person.ID)

}

Expand Down Expand Up @@ -46,6 +47,10 @@ extension PeopleEndpoint: Endpoint {
.appendingPathComponent("popular")
.appendingPage(page)

case .externalIDs(let personID):
return Self.basePath
.appendingPathComponent(personID)
.appendingPathComponent("external_ids")
}
}

Expand Down
19 changes: 19 additions & 0 deletions Sources/TMDb/People/PersonService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,23 @@ public final class PersonService {
return personList
}

///
/// Returns a collection of media databases and social links for a person.
///
/// - Parameters:
/// - personID: The identifier of the person.
///
/// - Returns: A collection of external links for the specificed person.
///
public func externalLinks(forPerson personID: Person.ID) async throws -> PersonExternalLinksCollection {
let linksCollection: PersonExternalLinksCollection
do {
linksCollection = try await apiClient.get(endpoint: PeopleEndpoint.externalIDs(personID: personID))
} catch let error {
throw TMDbError(error: error)
}

return linksCollection
}

}
1 change: 1 addition & 0 deletions Sources/TMDb/TMDb.docc/Extensions/PersonService.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@

- ``knownFor(forPerson:)``
- ``popular(page:)``
- ``externalLinks(forPerson:)``
14 changes: 14 additions & 0 deletions Tests/TMDbIntegrationTests/PersonIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,18 @@ final class PersonIntegrationTests: XCTestCase {
XCTAssertFalse(personList.results.isEmpty)
}

func testExternalLinks() async throws {
let personID = 115440

let linksCollection = try await personService.externalLinks(forPerson: personID)

XCTAssertEqual(linksCollection.id, personID)
XCTAssertNotNil(linksCollection.imdb)
XCTAssertNotNil(linksCollection.wikiData)
XCTAssertNil(linksCollection.facebook)
XCTAssertNotNil(linksCollection.instagram)
XCTAssertNotNil(linksCollection.twitter)
XCTAssertNotNil(linksCollection.tikTok)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation
@testable import TMDb

extension PersonExternalLinksCollection {

static func mock(
id: Person.ID,
imdb: IMDbLink? = nil,
wikiData: WikiDataLink? = nil,
facebook: FacebookLink? = nil,
instagram: InstagramLink? = nil,
twitter: TwitterLink? = nil,
tikTok: TikTokLink? = nil
) -> PersonExternalLinksCollection {
PersonExternalLinksCollection(
id: id,
imdb: imdb,
wikiData: wikiData,
facebook: facebook,
instagram: instagram,
twitter: twitter,
tikTok: tikTok
)
}

static var sydneySweeney: PersonExternalLinksCollection {
.mock(
id: 346698,
imdb: IMDbLink(imdbNameID: "nm2858875"),
wikiData: WikiDataLink(wikiDataID: "Q49561909"),
facebook: FacebookLink(facebookID: "sydney_sweeney"),
instagram: InstagramLink(instagramID: "sydney_sweeney"),
twitter: TwitterLink(twitterID: "sydney_sweeney"),
tikTok: TikTokLink(tikTokID: "syds_garage")
)
}

}
43 changes: 43 additions & 0 deletions Tests/TMDbTests/Models/PersonExternalLinksCollectionTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@testable import TMDb
import XCTest

final class PersonExternalLinksCollectionTests: XCTestCase {

func testDecodeReturnsPersonExternalLinksCollection() throws {
let expectedResult = PersonExternalLinksCollection(
id: 115440,
imdb: IMDbLink(imdbNameID: "nm2858875"),
wikiData: WikiDataLink(wikiDataID: "Q49561909"),
facebook: FacebookLink(facebookID: "sydney_sweeney"),
instagram: InstagramLink(instagramID: "sydney_sweeney"),
twitter: TwitterLink(twitterID: "sydney_sweeney"),
tikTok: TikTokLink(tikTokID: "syds_garage")
)

let result = try JSONDecoder.theMovieDatabase.decode(
PersonExternalLinksCollection.self,
fromResource: "person-external-ids"
)

XCTAssertEqual(result, expectedResult)
}

func testEncodeAndDecodeReturnsMovieExternalLinksCollection() throws {
let linksCollection = PersonExternalLinksCollection(
id: 115440,
imdb: IMDbLink(imdbNameID: "nm2858875"),
wikiData: WikiDataLink(wikiDataID: "Q49561909"),
facebook: FacebookLink(facebookID: "sydney_sweeney"),
instagram: InstagramLink(instagramID: "sydney_sweeney"),
twitter: TwitterLink(twitterID: "sydney_sweeney"),
tikTok: TikTokLink(tikTokID: "syds_garage")
)

let data = try JSONEncoder.theMovieDatabase.encode(linksCollection)

let result = try JSONDecoder.theMovieDatabase.decode(PersonExternalLinksCollection.self, from: data)

XCTAssertEqual(result, linksCollection)
}

}
Loading