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

Error while query users with emails that contains +(plus) character #453

Open
4 tasks done
Drendroyt opened this issue Dec 11, 2024 · 2 comments
Open
4 tasks done
Labels
type:bug Impaired feature or lacking behavior that is likely assumed

Comments

@Drendroyt
Copy link

Drendroyt commented Dec 11, 2024

New Issue Checklist

Issue Description

When I try to query object from my database (based on back4app) with email that contains +(plus) characters I got Parse Error "Object not found".
If I try to query objects with email that contains other special characters- "_" or "." everything working ok.

Steps to reproduce

  • create _User object with email that contains +(plus) character. For example "[email protected]"
  • fetch parse object from swift code (example with login below )
    func login(user: ParseUserObject) async throws {
        guard let email = user.email?.lowercased(), let password = user.password else {
            throw ParseError.Code.unknownError
        }
        // Fetch username from Parse(user provide only email/pass for login)
        var constraints = [QueryConstraint]()
        constraints.append(equalTo(key: "email", value: email))
        let query = ParseUserObject.query(constraints)
        let parseUser = try await query.first()
        // or you can use
        // let query = ParseUserObject.query("email" == email.lowercased())
        guard let username = parseUser.username else {
            throw ParseError.Code.unknownError
        }
        try await ParseUserObject.login(username: username, password: password)
        setIsAuthorized(true)
    }

Actual Outcome

try await query.first() throw an Parse Error (object not found)

Expected Outcome

Existing object

Environment

Xcode, iOS simulator (or physical iPhone), parse database based on back4app service

Client

  • Parse Swift SDK version: 4.14.2
  • Xcode version: 15.4
  • Operating system (iOS, macOS, watchOS, etc.): iOS
  • Operating system version: 18.0

Server

  • Parse Server version: 3.10.0
  • Operating system: Linux(maybe)
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): back4app server

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: 3.6
  • Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): back4app server

Logs

▿ ParseError code=101 error=Object not found on the server.

  • code : ParseSwift.ParseError.Code.objectNotFound
  • message : "Object not found on the server."
  • otherCode : nil
  • error : nil
Copy link

parse-github-assistant bot commented Dec 11, 2024

Thanks for opening this issue!

  • 🚀 You can help us to fix this issue faster by opening a pull request with a failing test. See our Contribution Guide for how to make a pull request, or read our New Contributor's Guide if this is your first time contributing.

@mtrezza mtrezza added the type:bug Impaired feature or lacking behavior that is likely assumed label Dec 11, 2024
@Drendroyt
Copy link
Author

as workaround for someone ...
You can you back4app REST API and decode object manually.

   func getUserWithEmail(_ email: String) async throws -> ParseUserObject? {
        // Configure query json value
        let whereQuery: [String: Any] = ["email": email]
        let queryJSONData = try JSONSerialization.data(withJSONObject: whereQuery, options: [])
        guard let jsonString = String(data: queryJSONData, encoding: .utf8) else { throw URLError(.badURL) }
        
        // Configure url and encode "+" character manualy
        var urlComponents = URLComponents()
        urlComponents.scheme = "https"
        urlComponents.host = "parseapi.back4app.com"
        urlComponents.path = "/users"
        urlComponents.queryItems = [.init(name: "where", value: jsonString)]
        urlComponents.percentEncodedQuery = urlComponents.percentEncodedQuery?
            .replacingOccurrences(of: "+", with: "%2B")

        guard let url = urlComponents.url else { throw URLError(.badURL) }
        
        // Performing request
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        guard let appID = Bundle.main.object(forInfoDictionaryKey: "ParseAppID") as? String,
              let restApiKey = Bundle.main.object(forInfoDictionaryKey: "ParseRestApiKey") as? String
        else {
            fatalError("Cannot find Parse SDK credentials")
        }
        
        request.addValue(appID, forHTTPHeaderField: "X-Parse-Application-Id")
        request.addValue(restApiKey, forHTTPHeaderField: "X-Parse-REST-API-Key")
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode)
        else {
            throw URLError(.badServerResponse)
        }
        let resposeArray = try JSONDecoder().decode(ParseResponse<ParseUserObject>.self, from: data)
        guard let parseUser = resposeArray.results.first else { throw ParseError.Code.userWithEmailNotFound}
        return parseUser
    }

struct ParseResponse<T>: Codable where T: Codable {
    let results: [T]
}

struct ParseUserObject: ParseUser {
    enum CodingKeys: String, CodingKey {
        case username, email, emailVerified, password, authData, originalData
        case objectId, createdAt, updatedAt, ACL, displayName
    }
        
    var username: String?
    var email: String?
    var emailVerified: Bool?
    var password: String?
    var authData: [String : [String : String]?]?
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?
    var displayName: String?
    
    init() {}
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.username = try container.decodeIfPresent(String.self, forKey: .username)
        self.email = try container.decodeIfPresent(String.self, forKey: .email)
        self.emailVerified = try container.decodeIfPresent(Bool.self, forKey: .emailVerified)
        self.password = try container.decodeIfPresent(String.self, forKey: .password)
        self.authData = try container.decodeIfPresent([String: [String: String]?].self, forKey: .authData)
        self.originalData = try container.decodeIfPresent(Data.self, forKey: .originalData)
        self.objectId = try container.decodeIfPresent(String.self, forKey: .objectId)
        self.ACL = try container.decodeIfPresent(ParseACL.self, forKey: .ACL)
        self.displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
        self.createdAt = try? container.decodeIfPresent(Date.self, forKey: .createdAt)
        self.updatedAt = try? container.decodeIfPresent(Date.self, forKey: .updatedAt)
    }
}

extension ParseUserObject {
    init(email: String, password: String) {
        self.email = email
        self.password = password
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:bug Impaired feature or lacking behavior that is likely assumed
Projects
None yet
Development

No branches or pull requests

2 participants