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: TVShow Content Ratings #188

Merged
merged 14 commits into from
Jul 16, 2024
62 changes: 62 additions & 0 deletions Sources/TMDb/Domain/Models/ContentRating.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// ContentRating.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

///
/// A model representing the content rating.
///
public struct ContentRating: Codable, Equatable, Hashable, Sendable {

///
/// ?
///
public let descriptors: [String]

///
/// The ISO 3166-1 country code.
///
public let countryCode: String

///
/// The content rating of the tv show
///
public let rating: String

/// Creates a content rating object.
///
/// - Parameters:
/// - descriptors: Array of....
/// - countryCode: ISO 3166-1 country code.
/// - rating: The content rating of the tv show
///
public init(descriptors: [String], countryCode: String, rating: String) {
self.descriptors = descriptors
self.countryCode = countryCode
self.rating = rating
}
}

extension ContentRating {
private enum CodingKeys: String, CodingKey {
case rating
case descriptors
case countryCode = "iso31661"
}
}
25 changes: 25 additions & 0 deletions Sources/TMDb/Domain/Models/ContentRatingResult.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// ContentRatingResult.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

struct ContentRatingResult: Codable, Equatable, Hashable, Sendable {
let results: [ContentRating]
let id: Int
}
2 changes: 1 addition & 1 deletion Sources/TMDb/Domain/Models/CrewMember.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import Foundation

///
/// A model representing a crew member..
/// A model representing a crew member.
///
public struct CrewMember: Identifiable, Codable, Equatable, Hashable, Sendable {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ContentRatingRequest.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

final class ContentRatingRequest: DecodableAPIRequest<ContentRatingResult> {

init(id: TVSeries.ID) {
let path = "/tv/\(id)/content_ratings"

super.init(path: path)
}

}
18 changes: 18 additions & 0 deletions Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ final class TMDbTVSeriesService: TVSeriesService {
return result.results[country]
}

func contentRatings(
forTVSeries tvSeriesID: TVSeries.ID,
country: String = "US"
) async throws -> ContentRating? {
let request = ContentRatingRequest(id: tvSeriesID)

let result: ContentRatingResult
do {
result = try await apiClient.perform(request)
} catch let error {
throw TMDbError(error: error)
}

return result.results.first { rating in
rating.countryCode == country
}
}

func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection {
let request = TVSeriesExternalLinksRequest(id: tvSeriesID)

Expand Down
17 changes: 17 additions & 0 deletions Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@
///
func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection

///
/// Returns the content rating of a TV series.
///
/// [TMDb API - TVSeries: Content ratings](https://developer.themoviedb.org/reference/tv-series-content-ratings)
///
/// - Parameters:
/// - tvSeriesID: The identifier of the TV series.
///
/// - Returns: A content rating for the specificed TV series.
///
func contentRatings(forTVSeries tvSeriesID: TVSeries.ID, country: String) async throws -> ContentRating?
}

public extension TVSeriesService {
Expand Down Expand Up @@ -285,4 +296,10 @@
try await watchProviders(forTVSeries: tvSeriesID, country: country)
}

func contentRatings(
forTVSeries tvSeriesID: TVSeries.ID,
country: String = "US"
) async throws -> ContentRating? {
try await contentRatings(forTVSeries: tvSeriesID, country: country)

Check warning on line 303 in Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift

View check run for this annotation

Codecov / codecov/patch

Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift#L302-L303

Added lines #L302 - L303 were not covered by tests
}
}
12 changes: 12 additions & 0 deletions Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,16 @@ final class TVSeriesServiceTests: XCTestCase {
XCTAssertNotNil(linksCollection.twitter)
}

func testContentRatings() async throws {
let tvSeriesID = 8592

let contentRatings = try await tvSeriesService.contentRatings(forTVSeries: tvSeriesID, country: "US")

XCTAssertNotNil(contentRatings)

if let contentRating = contentRatings {
XCTAssertEqual(contentRating.rating, "TV-14")
XCTAssertEqual(contentRating.countryCode, "US")
}
}
}
38 changes: 38 additions & 0 deletions Tests/TMDbTests/Domain/Models/ContentRatingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// ContentRatingTests.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

@testable import TMDb
import XCTest

final class ContentRatingTests: XCTestCase {

func testDecodeReturnsContentRating() throws {
let result = try JSONDecoder.theMovieDatabase.decode(ContentRating.self, fromResource: "content-rating")

XCTAssertEqual(result.descriptors, contentRating.descriptors)
XCTAssertEqual(result.countryCode, contentRating.countryCode)
XCTAssertEqual(result.rating, contentRating.rating)
}

private let contentRating = ContentRating(
descriptors: [],
countryCode: "US",
rating: "TV-14"
)
}
151 changes: 151 additions & 0 deletions Tests/TMDbTests/Domain/Models/ContentRatingsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//
// ContentRatingsTests.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

@testable import TMDb
import XCTest

final class ContentRatingsTests: XCTestCase {

func testDecodeReturnsContentRatingResult() throws {
let result = try JSONDecoder.theMovieDatabase.decode(ContentRatingResult.self, fromResource: "content-ratings")

XCTAssertEqual(result, contentRatings)
}

private let contentRatings = ContentRatingResult(
results: [
ContentRating(
descriptors: [],
countryCode: "US",
rating: "TV-14"
),
ContentRating(
descriptors: [],
countryCode: "AU",
rating: "M"
),
ContentRating(
descriptors: [],
countryCode: "RU",
rating: "12+"
),
ContentRating(
descriptors: [],
countryCode: "GB",
rating: "15"
),
ContentRating(
descriptors: [],
countryCode: "BR",
rating: "12"
),
ContentRating(
descriptors: [],
countryCode: "HU",
rating: "12"
),
ContentRating(
descriptors: [],
countryCode: "PH",
rating: "G"
),
ContentRating(
descriptors: [],
countryCode: "MX",
rating: "A"
),
ContentRating(
descriptors: [],
countryCode: "PT",
rating: "NR"
),
ContentRating(
descriptors: [],
countryCode: "ES",
rating: "16"
),
ContentRating(
descriptors: [],
countryCode: "FR",
rating: "16"
),
ContentRating(
descriptors: [],
countryCode: "CA",
rating: "PG"
),
ContentRating(
descriptors: [],
countryCode: "NL",
rating: "6"
),
ContentRating(
descriptors: [],
countryCode: "DE",
rating: "12"
),
ContentRating(
descriptors: [],
countryCode: "DE",
rating: "16"
),
ContentRating(
descriptors: [],
countryCode: "AU",
rating: "PG"
),
ContentRating(
descriptors: [],
countryCode: "KR",
rating: "15"
),
ContentRating(
descriptors: [],
countryCode: "AT",
rating: "12"
),
ContentRating(
descriptors: [],
countryCode: "CH",
rating: "12"
),
ContentRating(
descriptors: [],
countryCode: "PL",
rating: "16"
),
ContentRating(
descriptors: [],
countryCode: "HU",
rating: "16"
),
ContentRating(
descriptors: [],
countryCode: "CZ",
rating: "15+"
),
ContentRating(
descriptors: [],
countryCode: "RO",
rating: "15"
)
],
id: 8592
)
}
Loading
Loading