Skip to content

Commit

Permalink
FEATURE: Create Guest Session (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamayoung authored Jan 22, 2024
1 parent 896bd0b commit 83b26dd
Show file tree
Hide file tree
Showing 22 changed files with 509 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ clean:
rm -rf docs

.PHONY: format
format:
swiftlint --fix
swiftformat .

Expand Down
71 changes: 71 additions & 0 deletions Sources/TMDb/Authentication/AuthenticationService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// AuthenticationService.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

///
/// Provides an interface for authenticating with TMDb.
///
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public final class AuthenticationService {

private let apiClient: any APIClient

///
/// Creates an authentication service object.
///
public convenience init() {
self.init(
apiClient: TMDbFactory.authAPIClient
)
}

init(apiClient: some APIClient) {
self.apiClient = apiClient
}

///
/// Creates a guest session with TMDb.
///
/// Guest sessions are a special kind of session that give you some of the
/// functionality of an account, but not all. For example, some of the
/// things you can do with a guest session are; maintain a rated list, a
/// watchlist and a favourite list.
///
/// Guest sessions will automatically be deleted if they are not used
/// within 60 minutes of it being issued.
///
/// [TMDb API - Authentication: Create Guest Session](https://developer.themoviedb.org/reference/certifications-tv-list)
///
/// - Throws: TMDb error ``TMDbError``.
///
/// - Returns: A guest session.
///
public func createGuestSession() async throws -> GuestSession {
let session: GuestSession
do {
session = try await apiClient.get(endpoint: AuthenticationEndpoint.createGuestSession)
} catch let error {
throw TMDbError(error: error)
}

return session
}

}
41 changes: 41 additions & 0 deletions Sources/TMDb/Authentication/Endpoints/AuthenticationEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// AuthenticationEndpoint.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

enum AuthenticationEndpoint {

case createGuestSession

}

extension AuthenticationEndpoint: Endpoint {

private static let basePath = URL(string: "/authentication")!

var path: URL {
switch self {
case .createGuestSession:
Self.basePath
.appendingPathComponent("guest_session")
.appendingPathComponent("new")
}
}

}
8 changes: 8 additions & 0 deletions Sources/TMDb/Extensions/DateFormatter+TMDb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@ extension DateFormatter {
return dateFormatter
}

static var theMovieDatabaseAuth: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss' UTC '"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
return dateFormatter
}

}
7 changes: 7 additions & 0 deletions Sources/TMDb/Extensions/JSONDecoder+TMDb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,11 @@ extension JSONDecoder {
return decoder
}

static var theMovieDatabaseAuth: JSONDecoder {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(.theMovieDatabaseAuth)
return decoder
}

}
54 changes: 54 additions & 0 deletions Sources/TMDb/Models/GuestSession.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// GuestSession.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 a guest session.
///
public struct GuestSession: Codable, Equatable, Hashable {

///
/// Was session creation successful.
///
public let success: Bool

///
/// The identifier of this session.
///
public let guestSessionID: String

///
/// Date of session expiry.
///
public let expiresAt: Date

}

extension GuestSession {

private enum CodingKeys: String, CodingKey {

case success
case guestSessionID = "guestSessionId"
case expiresAt

}

}
11 changes: 11 additions & 0 deletions Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ``TMDb/AuthenticationService``

## Topics

### Creating am Authentication Service

- ``init()``

### Creating Sessions

- ``createGuestSession()``
5 changes: 5 additions & 0 deletions Sources/TMDb/TMDb.docc/TMDb.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,8 @@ For the TMDb API documentation, see
- ``Country``
- ``Department``
- ``Language``

### Authentication

- ``AuthenticationService``
- ``GuestSession``
14 changes: 14 additions & 0 deletions Sources/TMDb/TMDbFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ extension TMDbFactory {
)
}

static var authAPIClient: some APIClient {
TMDbAPIClient(
apiKey: TMDb.configuration.apiKey(),
baseURL: .tmdbAPIBaseURL,
httpClient: TMDb.configuration.httpClient(),
serialiser: authSerialiser,
localeProvider: localeProvider()
)
}

static func localeProvider() -> some LocaleProviding {
LocaleProvider(locale: .current)
}
Expand Down Expand Up @@ -81,4 +91,8 @@ extension TMDbFactory {
Serialiser(decoder: .theMovieDatabase)
}

private static var authSerialiser: some Serialiser {
Serialiser(decoder: .theMovieDatabaseAuth)
}

}
45 changes: 45 additions & 0 deletions Tests/TMDbIntegrationTests/AuthenticationIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// AuthenticationIntegrationTests.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 TMDb
import XCTest

final class AuthenticationIntegrationTests: XCTestCase {

var authenticationService: AuthenticationService!

override func setUpWithError() throws {
try super.setUpWithError()
try configureTMDb()
authenticationService = AuthenticationService()
}

override func tearDown() {
authenticationService = nil
super.tearDown()
}

func testCreateGuestSession() async throws {
let session = try await authenticationService.createGuestSession()

XCTAssertTrue(session.success)
XCTAssertNotEqual(session.guestSessionID, "")
}

}
66 changes: 66 additions & 0 deletions Tests/TMDbTests/Authentication/AuthenticationServiceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// AuthenticationServiceTests.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 AuthenticationServiceTests: XCTestCase {

var service: AuthenticationService!
var apiClient: MockAPIClient!

override func setUp() {
super.setUp()
apiClient = MockAPIClient()
service = AuthenticationService(apiClient: apiClient)
}

override func tearDown() {
apiClient = nil
service = nil
super.tearDown()
}

func testCreateGuestSessionReturnsGuestSession() async throws {
let expectedResult = GuestSession.mock(expiresAt: Date(timeIntervalSince1970: 1_705_956_596))

apiClient.result = .success(expectedResult)

let result = try await service.createGuestSession()

XCTAssertEqual(result, expectedResult)
XCTAssertEqual(apiClient.lastPath, AuthenticationEndpoint.createGuestSession.path)
}

func testCreateGuestSessionWhenErrorsThrowsError() async throws {
apiClient.result = .failure(.unknown)

var error: Error?
do {
_ = try await service.createGuestSession()
} catch let err {
error = err
}

let tmdbAPIError = try XCTUnwrap(error as? TMDbError)

XCTAssertEqual(tmdbAPIError, .unknown)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// AuthenticationEndpointTests.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 AuthenticationEndpointTests: XCTestCase {

func testCreateGuestSessionEndpointReturnsURL() throws {
let expectedURL = try XCTUnwrap(URL(string: "/authentication/guest_session/new"))

let url = AuthenticationEndpoint.createGuestSession.path

XCTAssertEqual(url, expectedURL)
}

}
Loading

0 comments on commit 83b26dd

Please sign in to comment.