<commit_before>//
//  Client.swift
//  PAYJP
//
//  Created by <NAME> on 2019/07/24.
//  Copyright © 2019 PAY, Inc. All rights reserved.
//

import Foundation

protocol ClientType {
    func request<Request: PAYJP.Request>(
        with request: Request,
        completion: ((Result<Request.Response, APIError>) -> Void)?
    ) -> URLSessionDataTask?
}

class Client: ClientType {

    static let shared = Client()

    private let session: URLSession
    private let callbackQueue: CallbackQueue
    private let jsonDecoder: JSONDecoder

    private init(
        session: URLSession = URLSession(configuration: .default),
        callbackQueue: CallbackQueue = CallbackQueue.dispatch(
        DispatchQueue(label: "jp.pay.ios", attributes: .concurrent)),
        jsonDecoder: JSONDecoder = JSONDecoder.shared
    ) {
        self.session = session
        self.callbackQueue = callbackQueue
        self.jsonDecoder = jsonDecoder
    }

    @discardableResult
    func request<Request: PAYJP.Request>(
        with request: Request,
        completion: ((Result<Request.Response, APIError>) -> Void)?
    ) -> URLSessionDataTask? {
        do {
            let urlRequest = try request.buildUrlRequest()
            let dataTask = self.session.dataTask(with: urlRequest) { [weak self] data, response, error in
                guard let self = self else { return }

                if error != nil && data == nil && response == nil {
                    completion?(Result.failure(.systemError(error!)))
                    return
                }

                guard let response = response as? HTTPURLResponse else {
                    completion?(Result.failure(.invalidResponse(nil)))
                    return
                }

                guard let data = data else {
                    completion?(Result.failure(.invalidResponse(response)))
                    return
                }

                guard error != nil else {
                    if response.statusCode == 200 {
                        do {
                            let result = try request.response(data: data, response: response)
                            completion?(Result.success(result))
                        } catch {
                            completion?(Result.failure(.invalidJSON(data, error)))
                        }
                    } else if response.statusCode == 303 {
                        if let tdsToken = self.createThreeDSecureToken(response: response) {
                            completion?(Result.failure(.requiredThreeDSecure(tdsToken)))
                        } else {
                            // TODO: tdsIdがない場合はエラーにする？
                            completion?(Result.failure(.invalidResponse(response)))
                        }
                    } else {
                        do {
                            let error = try self.jsonDecoder.decode(PAYErrorResult.self, from: data).error
                            completion?(Result.failure(.serviceError(error)))
                        } catch {
                            completion?(Result.failure(.invalidJSON(data, error)))
                        }
                    }
                    return
                }
            }

            self.callbackQueue.execute { dataTask.resume() }
            return dataTask
        } catch {
            completion?(Result.failure(.systemError(error)))
            return nil
        }
    }

    /// Locationヘッダからtds_idを取得する
    /// - Parameter response: HTTPURLResponse
    func createThreeDSecureToken(response: HTTPURLResponse) -> ThreeDSecureToken? {
        if let location = response.allHeaderFields["Location"] as? String {
            if let url = URL(string: location) {
                let components = url.pathComponents
                let filters = components.filter { $0.starts(with: "tds_") }
                if let tdsId = filters.first {
                    return ThreeDSecureToken(identifier: tdsId)
                }
            }
        }
        return nil
    }
}<commit_msg>fix check url path<commit_after>//
//  Client.swift
//  PAYJP
//
//  Created by <NAME> on 2019/07/24.
//  Copyright © 2019 PAY, Inc. All rights reserved.
//

import Foundation

protocol ClientType {
    func request<Request: PAYJP.Request>(
        with request: Request,
        completion: ((Result<Request.Response, APIError>) -> Void)?
    ) -> URLSessionDataTask?
}

class Client: ClientType {

    static let shared = Client()

    private let session: URLSession
    private let callbackQueue: CallbackQueue
    private let jsonDecoder: JSONDecoder

    private init(
        session: URLSession = URLSession(configuration: .default),
        callbackQueue: CallbackQueue = CallbackQueue.dispatch(
        DispatchQueue(label: "jp.pay.ios", attributes: .concurrent)),
        jsonDecoder: JSONDecoder = JSONDecoder.shared
    ) {
        self.session = session
        self.callbackQueue = callbackQueue
        self.jsonDecoder = jsonDecoder
    }

    @discardableResult
    func request<Request: PAYJP.Request>(
        with request: Request,
        completion: ((Result<Request.Response, APIError>) -> Void)?
    ) -> URLSessionDataTask? {
        do {
            let urlRequest = try request.buildUrlRequest()
            let dataTask = self.session.dataTask(with: urlRequest) { [weak self] data, response, error in
                guard let self = self else { return }

                if error != nil && data == nil && response == nil {
                    completion?(Result.failure(.systemError(error!)))
                    return
                }

                guard let response = response as? HTTPURLResponse else {
                    completion?(Result.failure(.invalidResponse(nil)))
                    return
                }

                guard let data = data else {
                    completion?(Result.failure(.invalidResponse(response)))
                    return
                }

                guard error != nil else {
                    if response.statusCode == 200 {
                        do {
                            let result = try request.response(data: data, response: response)
                            completion?(Result.success(result))
                        } catch {
                            completion?(Result.failure(.invalidJSON(data, error)))
                        }
                    } else if response.statusCode == 303 {
                        if let tdsToken = self.createThreeDSecureToken(response: response) {
                            completion?(Result.failure(.requiredThreeDSecure(tdsToken)))
                        } else {
                            // TODO: tdsIdがない場合はエラーにする？
                            completion?(Result.failure(.invalidResponse(response)))
                        }
                    } else {
                        do {
                            let error = try self.jsonDecoder.decode(PAYErrorResult.self, from: data).error
                            completion?(Result.failure(.serviceError(error)))
                        } catch {
                            completion?(Result.failure(.invalidJSON(data, error)))
                        }
                    }
                    return
                }
            }

            self.callbackQueue.execute { dataTask.resume() }
            return dataTask
        } catch {
            completion?(Result.failure(.systemError(error)))
            return nil
        }
    }

    /// Locationヘッダからtds_idを取得する
    /// - Parameter response: HTTPURLResponse
    /// - Returns: ThreeDSecureToken
    func createThreeDSecureToken(response: HTTPURLResponse) -> ThreeDSecureToken? {
        if let location = response.allHeaderFields["Location"] as? String,
            let url = URL(string: location) {
            if url.path.starts(with: "/v1/tds/") {
                let tdsId = url.pathComponents[3]
                return ThreeDSecureToken(identifier: tdsId)
            }
        }
        return nil
    }
}
{'fraction_non_alphanumeric': 0.0912590017382667, 'fraction_numerical': 0.004842314377948845, 'mean_word_length': 1.4685871897027276, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6701827', 'n_tokens_mistral': 2007, 'n_tokens_neox': 1872, 'n_words': 438}