Skip to content

Commit 3a5246e

Browse files
authored
FEATURE: TV Season Aggregate Credits (#175)
* Model and request * Update TV Season service * Add integration tests
1 parent a6cd279 commit 3a5246e

File tree

10 files changed

+353
-1
lines changed

10 files changed

+353
-1
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// TVSeasonAggregateCredits.swift
3+
// TMDb
4+
//
5+
// Copyright © 2024 Adam Young.
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an AS IS BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
20+
import Foundation
21+
22+
///
23+
/// A model representing a TV series aggregated credits.
24+
///
25+
/// A person can be both a cast member and crew member of the same show.
26+
///
27+
public struct TVSeasonAggregateCredits: Identifiable, Codable, Equatable, Hashable, Sendable {
28+
29+
///
30+
/// TV season identifier.
31+
///
32+
public let id: TVSeason.ID
33+
34+
///
35+
/// Cast members of the TV series.
36+
///
37+
public let cast: [AggregrateCastMember]
38+
39+
///
40+
/// Crew members of the TV series.
41+
///
42+
public let crew: [AggregrateCrewMember]
43+
44+
///
45+
/// Creates a TV season aggregrate credits object.
46+
///
47+
/// - Parameters:
48+
/// - id: TV season identifier.
49+
/// - cast: Cast members of the TV series.
50+
/// - crew: Crew members of the TV series.
51+
///
52+
public init(id: TVSeason.ID, cast: [AggregrateCastMember], crew: [AggregrateCrewMember]) {
53+
self.id = id
54+
self.cast = cast
55+
self.crew = crew
56+
}
57+
58+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// TVSeasonAggregateCreditsRequest.swift
3+
// TMDb
4+
//
5+
// Copyright © 2024 Adam Young.
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an AS IS BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
20+
import Foundation
21+
22+
final class TVSeasonAggregateCreditsRequest: DecodableAPIRequest<TVSeasonAggregateCredits> {
23+
24+
init(seasonNumber: Int, tvSeriesID: TVSeries.ID) {
25+
let path = "/tv/\(tvSeriesID)/season/\(seasonNumber)/aggregate_credits"
26+
27+
super.init(path: path)
28+
}
29+
30+
}

Sources/TMDb/Domain/Services/TVSeasons/TVSeasonService.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,39 @@ public final class TVSeasonService {
7171
return season
7272
}
7373

74+
///
75+
/// Returns the aggregate cast and crew of a TV season.
76+
///
77+
/// This call differs from the main credits call in that it does not return
78+
/// the newest season. Instead, it is a view of all the entire cast & crew
79+
/// for all episodes belonging to a TV season.
80+
///
81+
/// [TMDb API - TV Season: Aggregate Credits](https://developer.themoviedb.org/reference/tv-season-aggregate-credits)
82+
///
83+
/// - Parameters:
84+
/// - seasonNumber: The season number of a TV series.
85+
/// - tvSeriesID: The identifier of the TV series.
86+
///
87+
/// - Throws: TMDb error ``TMDbError``.
88+
///
89+
/// - Returns: Show credits for the matching TV season.
90+
///
91+
public func aggregateCredits(
92+
forSeason seasonNumber: Int,
93+
inTVSeries tvSeriesID: TVSeries.ID
94+
) async throws -> TVSeasonAggregateCredits {
95+
let request = TVSeasonAggregateCreditsRequest(seasonNumber: seasonNumber, tvSeriesID: tvSeriesID)
96+
97+
let credits: TVSeasonAggregateCredits
98+
do {
99+
credits = try await apiClient.perform(request)
100+
} catch let error {
101+
throw TMDbError(error: error)
102+
}
103+
104+
return credits
105+
}
106+
74107
///
75108
/// Returns the images that belong to a TV season.
76109
///

Sources/TMDb/TMDb.docc/Extensions/TVSeasonService.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
- ``details(forSeason:inTVSeries:)``
1212

13+
### Credits
14+
15+
- ``aggregateCredits(forSeason:inTVSeries:)``
16+
1317
### Media
1418

1519
- ``images(forSeason:inTVSeries:)``

Tests/TMDbIntegrationTests/AccountIntegrationTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ final class AccountIntegrationTests: XCTestCase {
5050
}
5151

5252
func testAddingAndRemovingFavouriteMovies() async throws {
53-
print("heloo")
5453
let accountDetails = try await accountService.details(session: session)
5554
let movieID = 550
5655

Tests/TMDbIntegrationTests/TVSeasonService.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,30 @@ final class TVSeasonServiceTests: XCTestCase {
4545
XCTAssertFalse((season.episodes ?? []).isEmpty)
4646
}
4747

48+
func testAggregateCredits() async throws {
49+
let seasonNumber = 2
50+
let tvSeriesID = 1399
51+
52+
let credits = try await tvSeasonService.aggregateCredits(forSeason: seasonNumber, inTVSeries: tvSeriesID)
53+
54+
XCTAssertEqual(credits.id, 3625)
55+
XCTAssertFalse(credits.cast.isEmpty)
56+
XCTAssertFalse(credits.crew.isEmpty)
57+
}
58+
4859
func testImages() async throws {
4960
let seasonNumber = 1
5061
let tvSeriesID = 1399
5162

63+
let imagesCollection = try await tvSeasonService.images(forSeason: seasonNumber, inTVSeries: tvSeriesID)
64+
65+
XCTAssertFalse(imagesCollection.posters.isEmpty)
66+
}
67+
68+
func testVideos() async throws {
69+
let seasonNumber = 1
70+
let tvSeriesID = 1399
71+
5272
let videoCollection = try await tvSeasonService.videos(forSeason: seasonNumber, inTVSeries: tvSeriesID)
5373

5474
XCTAssertFalse(videoCollection.results.isEmpty)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// TVSeasonAggregateCreditsTests.swift
3+
// TMDb
4+
//
5+
// Copyright © 2024 Adam Young.
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an AS IS BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
20+
@testable import TMDb
21+
import XCTest
22+
23+
final class TVSeasonAggregateCreditsTests: XCTestCase {
24+
25+
func testDecodeReturnsTVSeasonAggregateCredits() throws {
26+
let result = try JSONDecoder.theMovieDatabase.decode(
27+
TVSeriesAggregateCredits.self,
28+
fromResource: "tv-season-aggregate-credits"
29+
)
30+
31+
XCTAssertEqual(result.id, 3625)
32+
XCTAssertEqual(result.cast.count, 3)
33+
XCTAssertEqual(result.crew.count, 2)
34+
}
35+
36+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// TVSeasonAggregateCreditsRequestTests.swift
3+
// TMDb
4+
//
5+
// Copyright © 2024 Adam Young.
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an AS IS BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
20+
@testable import TMDb
21+
import XCTest
22+
23+
final class TVSeasonAggregateCreditsRequestTests: XCTestCase {
24+
25+
var request: TVSeasonAggregateCreditsRequest!
26+
27+
override func setUp() {
28+
super.setUp()
29+
request = TVSeasonAggregateCreditsRequest(seasonNumber: 2, tvSeriesID: 1)
30+
}
31+
32+
override func tearDown() {
33+
request = nil
34+
super.tearDown()
35+
}
36+
37+
func testPath() {
38+
XCTAssertEqual(request.path, "/tv/1/season/2/aggregate_credits")
39+
}
40+
41+
func testQueryItemsAreEmpty() {
42+
XCTAssertTrue(request.queryItems.isEmpty)
43+
}
44+
45+
func testMethodIsGet() {
46+
XCTAssertEqual(request.method, .get)
47+
}
48+
49+
func testHeadersIsEmpty() {
50+
XCTAssertTrue(request.headers.isEmpty)
51+
}
52+
53+
func testBodyIsNil() {
54+
XCTAssertNil(request.body)
55+
}
56+
57+
}

Tests/TMDbTests/Domain/Services/TVSeasons/TVSeasonServiceTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ final class TVSeasonServiceTests: XCTestCase {
5353
XCTAssertEqual(apiClient.lastRequest as? TVSeasonRequest, expectedRequest)
5454
}
5555

56+
func testAggregateCreditsReturnsTVSeasonCredits() async throws {
57+
let tvSeriesID = Int.randomID
58+
let expectedResult = TVSeasonAggregateCredits(id: 1, cast: [], crew: [])
59+
let seasonNumber = Int.randomID
60+
apiClient.addResponse(.success(expectedResult))
61+
let expectedRequest = TVSeasonAggregateCreditsRequest(seasonNumber: seasonNumber, tvSeriesID: tvSeriesID)
62+
63+
let result = try await service.aggregateCredits(forSeason: seasonNumber, inTVSeries: tvSeriesID)
64+
65+
XCTAssertEqual(result, expectedResult)
66+
XCTAssertEqual(apiClient.lastRequest as? TVSeasonAggregateCreditsRequest, expectedRequest)
67+
}
68+
5669
func testImagesReturnsImages() async throws {
5770
let seasonNumber = Int.randomID
5871
let tvSeriesID = Int.randomID
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{
2+
"cast": [
3+
{
4+
"adult": false,
5+
"gender": 2,
6+
"id": 22970,
7+
"known_for_department": "Acting",
8+
"name": "Peter Dinklage",
9+
"original_name": "Peter Dinklage",
10+
"popularity": 44.296,
11+
"profile_path": "/9CAd7wr8QZyIN0E7nm8v1B6WkGn.jpg",
12+
"roles": [
13+
{
14+
"credit_id": "5256c8b219c2956ff6047cd8",
15+
"character": "Tyrion 'The Halfman' Lannister",
16+
"episode_count": 10
17+
}
18+
],
19+
"total_episode_count": 10,
20+
"order": 0
21+
},
22+
{
23+
"adult": false,
24+
"gender": 2,
25+
"id": 239019,
26+
"known_for_department": "Acting",
27+
"name": "Kit Harington",
28+
"original_name": "Kit Harington",
29+
"popularity": 32.025,
30+
"profile_path": "/htGBMno71BJAEGF3Y9f62MdA3Yt.jpg",
31+
"roles": [
32+
{
33+
"credit_id": "5256c8af19c2956ff6047af6",
34+
"character": "Jon Snow",
35+
"episode_count": 10
36+
}
37+
],
38+
"total_episode_count": 10,
39+
"order": 1
40+
},
41+
{
42+
"adult": false,
43+
"gender": 2,
44+
"id": 512991,
45+
"known_for_department": "Acting",
46+
"name": "Richard Madden",
47+
"original_name": "Richard Madden",
48+
"popularity": 44.12,
49+
"profile_path": "/kC7X9LgAtJfpxUBRtVwaVTEXomH.jpg",
50+
"roles": [
51+
{
52+
"credit_id": "5256c8af19c2956ff6047b1a",
53+
"character": "Robb Stark",
54+
"episode_count": 10
55+
}
56+
],
57+
"total_episode_count": 10,
58+
"order": 4
59+
}
60+
],
61+
"crew": [
62+
{
63+
"adult": false,
64+
"gender": 0,
65+
"id": 982992,
66+
"known_for_department": "Art",
67+
"name": "Ashleigh Jeffers",
68+
"original_name": "Ashleigh Jeffers",
69+
"popularity": 5.33,
70+
"profile_path": null,
71+
"jobs": [
72+
{
73+
"credit_id": "622da5c312970c007974b75d",
74+
"job": "Art Direction",
75+
"episode_count": 9
76+
}
77+
],
78+
"department": "Art",
79+
"total_episode_count": 9
80+
},
81+
{
82+
"adult": false,
83+
"gender": 2,
84+
"id": 37301,
85+
"known_for_department": "Art",
86+
"name": "Frank Walsh",
87+
"original_name": "Frank Walsh",
88+
"popularity": 5.677,
89+
"profile_path": null,
90+
"jobs": [
91+
{
92+
"credit_id": "622da5b2534661001b7d306f",
93+
"job": "Supervising Art Director",
94+
"episode_count": 9
95+
}
96+
],
97+
"department": "Art",
98+
"total_episode_count": 9
99+
}
100+
],
101+
"id": 3625
102+
}

0 commit comments

Comments
 (0)