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

When using beforeSave on object with a Pointer column - "error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object" #65

Open
jaysonng opened this issue Feb 17, 2024 · 45 comments · Fixed by #71
Labels
bug Something isn't working

Comments

@jaysonng
Copy link
Contributor

jaysonng commented Feb 17, 2024

When using beforeSave trigger on an object that has a pointer to another object like so:

struct Test: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var name: String?
    var test1: Test1?
}

struct Test1: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var description: String?
    var test2: Test2?
    var test3: Test3?
}

Doing nothing but just having the beforeSave trigger on produces a schema mismatch error where it expects a Pointer to the sub-object.

func beforeSaveTest(req: Request) async throws -> HookResponse<Test> {

        if let error: HookResponse<Test> = checkHeaders(req) { return error }
        
        var parseRequest = try req.content.decode(HookTrigObjReq<User, Test>.self)
        let options      = try parseRequest.options(req)
        
        if parseRequest.user != nil {
            parseRequest = try await parseRequest.hydrateUser(options: options, request: req)
        }
        
        guard let object = parseRequest.object else {
            return ParseHookResponse(error: .init(code: .missingObjectId,
                                                  message: "Object not sent in request."))
        }
        
        return HookResponse(success: object)
        
    }

I encountered this as I was trying to save a complex object with multiple pointers and tested with these Test objects getting the same result.

error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object
@jaysonng jaysonng changed the title When using beforeSave - error: "schema mismatch for Test.test1; expected Pointer<Test1> but got Object" When using beforeSave on object with a Pointer column - "error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object" Feb 17, 2024
@cbaker6 cbaker6 added the wontfix This will not be worked on label Feb 17, 2024
@cbaker6
Copy link
Member

cbaker6 commented Feb 17, 2024

See my response here: netreconlab/Parse-Swift#151 (comment)

@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 17, 2024

See my response here: netreconlab/Parse-Swift#151 (comment)

I think this is a different problem. it wasn't a matter of it having the same schema or anything like that.

I was just passing an already existing object, which has pointed to another object (already existing)

it didn't have to create anything. But passing the object through Parse-Server-Swift BeforeSave causes this error.

i just tested beforeSave on JS cloudcode and it updated fine.

This has something to do with Pointer and XXXXX object and how ParseSwift handles it. I'm just not sure exactly where it goes wrong.

@jaysonng
Copy link
Contributor Author

Screenshot 2024-02-17 at 11 36 49 PM

As an example. I just tried to update "wohoo" to "wohoo1"

and again got this error:

error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object

Even when I try it on Dashboard.

Screenshot 2024-02-17 at 11 39 58 PM

@cbaker6
Copy link
Member

cbaker6 commented Feb 17, 2024

I don’t see a “beforeSave” trigger in the code you provided. I do see “beforeSaveTest” but that’s not a beforeSave trigger

@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 17, 2024

I don’t see a “beforeSave” trigger in the code you provided. I do see “beforeSaveTest” but that’s not a beforeSave trigger

sorry, I call beforeSaveTest using the boot routes builder like so.

    func boot(routes: RoutesBuilder) throws {
        
        let lokalityGroup = routes.grouped("lokality")
        //: Triggers
        lokalityGroup.post("save", "before", object: Lokality.self,
                           trigger: .beforeSave, use: beforeSave)
        lokalityGroup.post("save", "before", object: Test.self,
                           trigger: .beforeSave, use: beforeSaveTest)
   }

with logs

[ INFO ] POST /lokality/save/before [request-id: 2049B8FB-60B5-49E9-AC65-8A80302F75DA]

so it does get called on .save()

@cbaker6
Copy link
Member

cbaker6 commented Feb 17, 2024

It still looks like the same problem as #65 (comment) to me. ParseServerSwift uses ParseSwift, the beforeSave webhook is called before an object is created on the server so it will run into the same issue. I think you can improve on your systems design to circumvent this, like not saving empty items for no reason. You should save objects when they actually have data. I gave some examples in the previous links. If you insist on keeping your design, you may need to remove your “beforeSave” code using ParseServerSwift, and us the JS Cloud Code for that part.

You could also never create empty objects on the client, use ParseServerSwift beforeSave to create them if they are nil

@jaysonng
Copy link
Contributor Author

It's not empty though.

They're already there and existing.

@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 17, 2024

Screenshot 2024-02-17 at 11 36 49 PM

See this. The info is already there on the dashboard. I'm just trying to update the row with objectId DrhQdVNrhs

"Wohoo" is a string that's already existing.

The Test1 is existing and pointing to an existing object.

When i try to update "wohoo" to "wohoo1" I get the error.

@jaysonng
Copy link
Contributor Author

So it's not a matter of creating empty objects this time around but how ParseServerSwift handles saving a Pointer but receiving an object in beforeSave.

Somehow it sees the struct and thinks it's a 'Pointer' but really it's getting a 'Test1' - in beforeSave.

@jaysonng
Copy link
Contributor Author

I'll give it a try on more of my other classes that have the same setup of pointers a do a beforeSave that already has existing data and see if i get it saving.

The Test classes I use are for posting here on Github but I initially encountered the error on my other classes.

@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 18, 2024

I got it to save just now by changing var test1: Test1? on my Test struct by changing it to a Pointer<Test1>? and it saved as expected on ParseDashboard.

The save was just updating an existing object.

struct Test: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var name: String?
    var test1: Test1?
}

to

var test1: Pointer<Test1>?

If that gives you an idea of where we can look?

This "fix" isn't workable as I'd have to toPointer() every time I use the object in my code.

@cbaker6
Copy link
Member

cbaker6 commented Feb 18, 2024

I recommend looking at the unit tests and try to create a PR which replicates your scenario. You should be able to do it in the ParseSwift repo as it supports all of the hooks. The test case will most likely go into this file: https://github.com/netreconlab/Parse-Swift/blob/main/Tests/ParseSwiftTests/ParseHookTriggerTests.swift.

@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 18, 2024

I recommend looking at the unit tests and try to create a PR which replicates your scenario. You should be able to do it in the ParseSwift repo as it supports all of the hooks. The test case will most likely go into this file: https://github.com/netreconlab/Parse-Swift/blob/main/Tests/ParseSwiftTests/ParseHookTriggerTests.swift.

My coding skills haven't gotten that far yet. 😄 I mean I've done a few. It's how I test the methods. but not sure how to test the hooks yet directly like in that ParseHookTriggerTest.swift. I'll look into it though.

thanks

@cbaker6
Copy link
Member

cbaker6 commented Feb 18, 2024

What happens when you try the following?

test1.set(\.name, “woohoo1”) and then save? When updating individual properties on an object that’s already created, it’s better to use set anyways: https://github.com/netreconlab/Parse-Swift/blob/b3169f2b438df9bb1d14935246be0a462c97c42f/ParseSwift.playground/Pages/1%20-%20Your%20first%20Object.xcplaygroundpage/Contents.swift#L241-L261

@jaysonng
Copy link
Contributor Author

What happens when you try the following?

test1.set(\.name, “woohoo1”) and then save? When updating individual properties on an object that’s already created, it’s better to use set anyways: https://github.com/netreconlab/Parse-Swift/blob/b3169f2b438df9bb1d14935246be0a462c97c42f/ParseSwift.playground/Pages/1%20-%20Your%20first%20Object.xcplaygroundpage/Contents.swift#L241-L261

func testTest() async {
        do {

            let testQuery = Test.query()
            var result = try await testQuery.first(options: [.usePrimaryKey])
//            result.name = "wohoo1234"
            result.set(\.name, to: "wohoo4321")
            try await result.save(options: [.usePrimaryKey])

            print("Successfully updated: \(result.objectId.logable)")

        } catch {
            print("error: \(error.localizedDescription)")
            XCTAssertNil("Update Test failed")
        }

    }

still the same error
error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object

@jaysonng
Copy link
Contributor Author

I'm still trying to figure out how to do a Unit Test without an actual database though - for the PR.

@cbaker6
Copy link
Member

cbaker6 commented Feb 19, 2024

I'm still trying to figure out how to do a Unit Test without an actual database though - for the PR.

The test file I referenced before shows how to do this:

Server response: https://github.com/netreconlab/Parse-Swift/blob/b3169f2b438df9bb1d14935246be0a462c97c42f/Tests/ParseSwiftTests/ParseHookTriggerTests.swift#L188-L201

Expected JSON: https://github.com/netreconlab/Parse-Swift/blob/b3169f2b438df9bb1d14935246be0a462c97c42f/Tests/ParseSwiftTests/ParseHookTriggerTests.swift#L72-L82

There are 1000+ examples in the current unit tests. Look for one that seems more intuitive, then go to more complex

@jaysonng
Copy link
Contributor Author

How do I actually trigger the beforeSave Trigger in a test? I can't seem to find an existing test that actually calls it or runs it. everything seems to be just creating / updating hooks.

@jaysonng
Copy link
Contributor Author

@cbaker6

I haven't been able to successfully do a unit test with my case but I have figured out what's causing the problem.

In JS CloudCode, the object I get out of the Request vs the paramsRequest.object (which was decoded) is structured differently.

In JS CloudCode, the object keeps the __type: Pointer on the object. In ParseServerSwift, the line

parseRequest = req.content.decode(...)

converts the Pointer to just an Object which is then what we pass to the ParseHookResponse at the end of the beforeSave

return ParseHookResponse(success: object)

Question is, how do I pull out the data I need from the Request (while in the beforeSave trigger function) without decoding it or decode it in a way that I keep the Pointer structure?

func beforeSaveTest(req: Request) async throws -> ParseHookResponse<Test> {
        
        if let error: HookResponse<Test> = checkHeaders(req) { return error }
        
// object structure is changed here which JS validation errors out later in SchemaController.js
        var parseRequest = try req.content.decode(ParseHookTriggerObjectRequest<User, Test>.self)
        let options      = try parseRequest.options(req)
        
        if parseRequest.user != nil {
            parseRequest = try await parseRequest.hydrateUser(options: options, request: req)
        }
        
        guard var object = parseRequest.object else {
            return ParseHookResponse(error: .init(code: .missingObjectId,
                                                  message: "Object not sent in request."))
        }

        return HookResponse(success: object)
    }

SchemaController.js in parse-server

@cbaker6
Copy link
Member

cbaker6 commented Feb 22, 2024

I think I may understand where the issue is at now, at this line:

// Parse uses tailored encoders/decoders. These can be retrieved from any ParseObject
ContentConfiguration.global.use(encoder: User.getJSONEncoder(), for: .json)

Change to:

// Parse uses tailored encoders/decoders. These can be retrieved from any ParseObject
ContentConfiguration.global.use(encoder: User.getEncoder() /* Change the encoder here */, for: .json)

Delete all of your hooks and functions before connecting the updated ParseServer and let me know if this works. It's possible this may introduce other issues that weren't originally there since parse-server-swift is only connecting to a ParseServer and not something that expects traditional JSON. If there are new issues, we will need to look further into how to better utilize Vapors encoder configuration.

@cbaker6 cbaker6 added bug Something isn't working and removed wontfix This will not be worked on labels Feb 22, 2024
@jaysonng
Copy link
Contributor Author

jaysonng commented Feb 23, 2024

Error when building Parse-Swift-Server:

Screenshot 2024-02-23 at 1 33 02 PM

needed to add as! ContentCoder because of this error:

Screenshot 2024-02-23 at 1 33 53 PM

@jaysonng
Copy link
Contributor Author

Commenting out line 50, allows me to build and run Parse-Server-Swift but I get the same schema error on calling beforeSave.

Screenshot 2024-02-23 at 5 13 44 PM

@jaysonng
Copy link
Contributor Author

I've been trying to pull the data out "as is" of the HTTP header Request but couldn't figure it out.

I'm not well versed in decoding yet. usually I just decode json.

@cbaker6
Copy link
Member

cbaker6 commented Feb 23, 2024

It doesn't look like it's a decoding issue, it looks more like an encoding issue because the parse-server expects Parse json to look a particular way. Objects that can be pointers should be sent as pointers which is why #65 (comment) works. I'll look into a fix when I get some time next week.

If you you really want to get it working now, create two objects for ParseObjects that contain pointers:

// This is the same as the original, but add the `convertForSending` method
struct Test: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var name: String?
    var test1: Test1?

     func convertForSending() -> TestForSendingOnly {
           var convertedObject = TestForSendingOnly()
           // Copy all properties manually
           convertedObject.createdAt = try? self.createdAt
           // ...
           convertedObject.test1 = try? self.test1.toPointer()

           return convertedObject
    }
}

// This almost looks like the original, but has pointers
struct TestForSendingOnly: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var name: String?
    var test1: Pointer<Test1>?
}

// Cloud Code
func beforeSaveTest(req: Request) async throws -> ParseHookResponse<TestForSendingOnly> {
        
        if let error: HookResponse<Test> = checkHeaders(req) { return error }
        
        var parseRequest = try req.content.decode(ParseHookTriggerObjectRequest<User, Test>.self)
        let options      = try parseRequest.options(req)
        
        if parseRequest.user != nil {
            parseRequest = try await parseRequest.hydrateUser(options: options, request: req)
        }
        
        guard var object = parseRequest.object else {
            return ParseHookResponse(error: .init(code: .missingObjectId,
                                                  message: "Object not sent in request."))
        }

        let convertedObject = object.convertForSending()
        return HookResponse(success: convertedObject)
}

@jaysonng
Copy link
Contributor Author

ah didn't think of that workaround. but given that I have lots of objects with lots of pointers, I think I'll wait for your fix for this then.

thanks again!

@cbaker6
Copy link
Member

cbaker6 commented Jul 12, 2024

@jaysonng can you test out #71 by upgrading to https://github.com/netreconlab/parse-server-swift/releases/tag/0.11.1 and let me know if it addresses the issue?

@cbaker6 cbaker6 reopened this Jul 12, 2024
@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 2, 2024

hi @cbaker6 ! sorry I haven't checked github in a while. I'll update to the latest versions and test this out. thanks!

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 2, 2024

I just updated to 0.12 and haven't totally tested it out with triggers. but, I do get this error:

Screenshot 2024-08-02 at 11 13 44 PM

which isn't there on a clean install of 0.12. everything runs smoothly.

But once I place my files in, the error comes up.

I'm not sure if it has something to do with my User object being extended and having more custom properties? I had to delete the User.swift file that comes with 0.12 and use my own.

I test this error by switching branches in my private repo. Because it's private, I couldn't just pull from a public fork of 0.12 so i downloaded the files and just placed it in my existing project.

Do you have a quick insight on why I'm getting this error ?

still the same error I got back in February (#65 (comment))

@cbaker6
Copy link
Member

cbaker6 commented Aug 2, 2024

You are not adding the files correctly to your repo, specifically the extension of ParseEncoder that conforms to ContentEncoder, https://github.com/netreconlab/parse-server-swift/pull/71/files. You shouldn’t have this problem if you are using the repo as a package in your project as the versions package up all of the files.

You can usually tell if you’ve copied something wrong by testing main repo, if the issue isn’t there, it’s most likely your own code

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 2, 2024

You are not adding the files correctly to your repo, specifically the extension of ParseEncoder that conforms to ContentEncoder, https://github.com/netreconlab/parse-server-swift/pull/71/files. You shouldn’t have this problem if you are using the repo as a package in your project as the versions package up all of the files.

You can usually tell if you’ve copied something wrong by testing main repo, if the issue isn’t there, it’s most likely your own code

ok ill try it again. thanks!

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 2, 2024

thanks I see it. I overwrote the Parse+Vapor.swift file accidentally.

@cbaker6
Copy link
Member

cbaker6 commented Aug 2, 2024

I recommend you start using the package. I changed a number of files in recent updates and there’s no telling what files I’ll change in the future. IMO, your current way of maintaining your private repo isn’t sustainable and you will run into similar issues in the future. Your current way also makes it hard for me to diagnose issues as I don’t know what version you really have in your repo since you are copy/pasting code.

You should be doing something like https://github.com/netreconlab/parse-server-swift?tab=readme-ov-file#creating-your-cloud-code-app-with-parseserverswift

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 4, 2024

Hi @cbaker6 , I need some help with the setup using ParseServerSwift.

for some reason, my custom routes.swift in my project folder isn't being used and the default routes.swift file is being used when I start and run my app.

Screenshot 2024-08-04 at 7 27 59 PM

What setting should I be looking at? Something with the targets?

// swift-tools-version:5.10
import PackageDescription

let package = Package(
    name: "BusogCloud",
    platforms: [
        .iOS(.v13),
        .macCatalyst(.v13),
        .macOS(.v10_15),
        .tvOS(.v13),
        .watchOS(.v6)
    ],
    products: [
            .library(name: "BusogCloud", targets: ["BusogCloud"])
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
        // 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
        .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
        .package(url: "https://github.com/netreconlab/Parse-Swift.git", from: "5.11.2"),
        .package(url: "https://github.com/netreconlab/ParseServerSwift", .upToNextMajor(from: "0.12.0")),
        .package(url: "https://github.com/lokalnewsapp/Countries", .upToNextMajor(from: "1.0.0"))
    ],
    targets: [
        .target(name: "BusogCloud",
        dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "NIOCore", package: "swift-nio"),
                .product(name: "NIOPosix", package: "swift-nio"),
                .product(name: "ParseSwift", package: "Parse-Swift"),
                .product(name: "ParseServerSwift", package: "ParseServerSwift"),
                .product(name: "Countries", package: "Countries")
            ]
        ),
        .executableTarget(
            name: "App",
            dependencies: [.target(name: "BusogCloud")],
            swiftSettings: [
                // Enable better optimizations when building in Release configuration. Despite the use of
                // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
                // builds. See <https://github.com/swift-server/guides/blob/main/docs/building.md#building-for-production> for details.
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .testTarget(
            name: "AppTests",
            dependencies: [
                .target(name: "App"),
                .product(name: "XCTVapor", package: "vapor"),
            ],
            swiftSettings: swiftSettings
        )
    ]
)

var swiftSettings: [SwiftSetting] { [
    .enableUpcomingFeature("DisableOutwardActorInference"),
    .enableExperimentalFeature("StrictConcurrency"),
] }

This is my Package.swift file if it helps.

Thanks!

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 4, 2024

I know its not working because when i run the app, the default hooks and triggers that i dont have in my routes.swift gets loaded up into webhooks and none of my custom hooks get loaded.

@cbaker6
Copy link
Member

cbaker6 commented Aug 4, 2024

You will need to show what configuration function you are using, but if the's the one provided by ParseServerSwift, you will need to create your own and use your own routes until I provide the ability to pass routes:

// register routes
try routes(app)

See #74 and https://github.com/netreconlab/parse-server-swift/releases/tag/0.13.0 for fix of using your own routes.

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 5, 2024

Screenshot 2024-08-05 at 6 58 52 PM
entrypoint.swift

error: cannot find my custom routes function which is in my BusogCloud folder routes.swift file.

Screenshot 2024-08-05 at 6 59 18 PM

and I also added my own configure.swift file as you suggested:
Screenshot 2024-08-05 at 6 59 30 PM

but I'm currently still stuck at entrypoint in App executabletarget not seeing BusogCloud target files.

Seems like a Vapor thing I'm not understanding. I tried a basic new Vapor app and built a new target with dependencies just like in parseserverswift but it can't see the configure file when I split it up between App folder and target folder.

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 5, 2024

finally got past this problem by just using the default App folder provided by Vapor's default project as the executableTarget and not modifying Package.swift to use .target() like suggested in the current ReadMe file.

My current Package.swift file just reads

let package = Package(
    name: "BusogCloud",
    platforms: [
        .iOS(.v13),
        .macCatalyst(.v13),
        .macOS(.v10_15),
        .tvOS(.v13),
        .watchOS(.v6)
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
        // 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
        .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
        .package(url: "https://github.com/netreconlab/Parse-Swift.git", from: "5.11.2"),
        .package(url: "https://github.com/netreconlab/ParseServerSwift", .upToNextMajor(from: "0.12.0")),
        .package(url: "https://github.com/lokalnewsapp/Countries", .upToNextMajor(from: "1.0.0"))
    ],
    targets: [
        .executableTarget(
            name: "App",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "NIOCore", package: "swift-nio"),
                .product(name: "NIOPosix", package: "swift-nio"),
                .product(name: "ParseSwift", package: "Parse-Swift"),
                .product(name: "ParseServerSwift", package: "ParseServerSwift"),
                .product(name: "Countries", package: "Countries")
                ],
            swiftSettings: [
                // Enable better optimizations when building in Release configuration. Despite the use of
                // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
                // builds. See <https://github.com/swift-server/guides/blob/main/docs/building.md#building-for-production> for details.
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .testTarget(
            name: "AppTests",
            dependencies: [
                .target(name: "App"),
                .product(name: "XCTVapor", package: "vapor"),
            ],
            swiftSettings: swiftSettings
        )
    ]
)

I'm not sure if this is a bug on the relationship between Vapor, packages and ParseServerSwift (as a dependency) where I can't use targets.

I'm off to test the triggers now. thanks.

@cbaker6
Copy link
Member

cbaker6 commented Aug 5, 2024

error: cannot find my custom routes function which is in my BusogCloud folder routes.swift file.

You probably just need to make your routes function public,

public func exampleRoutes(_ app: Application) throws {

and I also added my own configure.swift file as you suggested:

This was only needed if you are using version <0.13, if you are using >=0.13, you can pass any Routes method to

try await parseServerSwiftConfigure(
app,
using: exampleRoutes
)
as I showed in #74

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 6, 2024

regarding dockerfile, should I be copying the one in ParseServerSwift package for my project or the default one by Vapor?

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 6, 2024

You probably just need to make your routes function public,

I just tried this and it worked. I also needed to add import BusogCloud (my app).

thanks! :) the busogRoutes function is being seen now.

@cbaker6
Copy link
Member

cbaker6 commented Aug 6, 2024

regarding dockerfile, should I be copying the one in ParseServerSwift package for my project or the default one by Vapor?

The docker file in ParseServerSwift was generated from an older version of vapor. It was then updated to work with Parse. It’s possible, this file may not be using the latest docker settings Vapor suggets. In this case, since you generated your current vapor project with the latest version of vapor (I’m assumming) then you can compare and submit PR’s with the changes to this repo in the future.

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 6, 2024

@jaysonng can you test out #71 by upgrading to https://github.com/netreconlab/parse-server-swift/releases/tag/0.11.1 and let me know if it addresses the issue?

Hi @cbaker6, unfortunately I'm still getting the same error:
Screenshot 2024-08-07 at 12 58 34 AM
Screenshot 2024-08-07 at 12 50 00 AM

these screenshots are from the ParseDashboard error when just editing an existing data.

Changing an Object's property to Pointer<XXXX> does fix it, but then I have to change every reference to it by using .toPointer()

I have updated to the latest versions:
Screenshot 2024-08-07 at 1 02 59 AM

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 7, 2024

This is for running beforeSave trigger by the way.

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 7, 2024

  1. Uncaught exception Error when trying to update a field in a Test object with this beforeSave code which returns ParseHookResponse. ( this one is new to me, I didn't notice this before )
    func beforeSaveTest(req: Request) async throws -> ParseHookResponse<Bool> {
        
        if let error: ParseHookResponse<Bool> = checkHeaders(req) {
            return error
        }
        
        let parseRequest = try req.content
            .decode(ParseHookTriggerObjectRequest<User, Test>.self)

        req.logger.info("A LiveQuery event occured: \(parseRequest)")
        return ParseHookResponse(success: true)

    }

I get an error but does not crash my ParseServerSwift project.

info: beforeSave triggered for Test for user undefined:
  Input: {"name":"jayson21","createdAt":"2024-02-18T00:27:35.235Z","updatedAt":"2024-08-06T16:57:26.391Z","test1":{"__type":"Pointer","className":"Test1","objectId":"rRW7jehY8s"},"objectId":"yMzqHQwyhB"}
  Result: {"object":true} {"className":"Test","triggerType":"beforeSave"}
error: Uncaught internal server error. Cannot create property 'updatedAt' on boolean 'true' {"stack":"TypeError: Cannot create property 'updatedAt' on boolean 'true'\n    at /Users/jayson/Projects/Busog/parse-server/node_modules/parse-server/lib/RestWrite.js:316:29\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"}
TypeError: Cannot create property 'updatedAt' on boolean 'true'
    at /Users/jayson/Projects/Busog/parse-server/node_modules/parse-server/lib/RestWrite.js:316:29
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:405:5)
    at ServerResponse.setHeader (node:_http_outgoing:648:11)
    at ServerResponse.header (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/response.js:795:10)
    at ServerResponse.send (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/response.js:175:12)
    at errorHandler (/Users/jayson/Projects/Busog/parse-server/files/errorHandler.js:6:21)
    at Layer.handle_error (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/router/layer.js:71:5)
    at trim_prefix (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/router/index.js:326:13)
    at /Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/router/index.js:286:9
    at Function.process_params (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/router/index.js:346:12)
    at next (/Users/jayson/Projects/Busog/parse-server/node_modules/express/lib/router/index.js:280:10)
  1. When I change ParseHookResponse to ParseHookResponse There is NO ERROR. it updates the object properly.
    note here that I'm passing a new Test object to ParseHookResponse.
 func beforeSaveTest(req: Request) async throws -> ParseHookResponse<Test> {
        
        if let error: ParseHookResponse<Test> = checkHeaders(req) {
            return error
        }
        
        let parseRequest = try req.content
            .decode(ParseHookTriggerObjectRequest<User, Test>.self)

        req.logger.info("A LiveQuery event occured: \(parseRequest)")
        
        return ParseHookResponse(success: Test())

    }
  1. The main error is when we pass and return the object param taken from parseRequest like so:
 func beforeSaveTest(req: Request) async throws -> HookResponse<Test> {

        if let error: ParseHookResponse<Test> = checkHeaders(req) {
            return error
        }

        var parseRequest = try req.content
            .decode(ParseHookTriggerObjectRequest<User, Test>.self)
    
        let options      = try parseRequest.options(req)
        
        if parseRequest.user != nil {
            parseRequest = try await parseRequest.hydrateUser(options: options, request: req)
        }
        
        guard let object = parseRequest.object else {
            return ParseHookResponse(error: .init(code: .missingObjectId,
                                                  message: "Object not sent in request."))
        }
        
        req.logger.info("A LiveQuery event occured: \(parseRequest)")
        return ParseHookResponse(success: object)

    }

The full error:

info: beforeSave triggered for Test for user undefined:
  Input: {"name":"jayson12s2","createdAt":"2024-02-18T00:27:35.235Z","updatedAt":"2024-08-07T02:31:56.122Z","test1":{"__type":"Pointer","className":"Test1","objectId":"rRW7jehY8s"},"objectId":"yMzqHQwyhB"}
  Result: {"object":{"name":"jayson12s2","objectId":"yMzqHQwyhB","test1":{"objectId":"rRW7jehY8s"}}} {"className":"Test","triggerType":"beforeSave"}
error: schema mismatch for Test.test1; expected Pointer<Test1> but got Object {"code":111,"stack":"Error: schema mismatch for Test.test1; expected Pointer<Test1> but got Object\n    at SchemaController.enforceFieldExists (/Users/jayson/Projects/Busog/parse-server/node_modules/parse-server/lib/Controllers/SchemaController.js:1121:15)\n    at SchemaController.validateObject (/Users/jayson/Projects/Busog/parse-server/node_modules/parse-server/lib/Controllers/SchemaController.js:1252:28)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"}

and of course I can't just .toPointer() object to pass to ParseHookResponse.

@jaysonng
Copy link
Contributor Author

jaysonng commented Aug 7, 2024

is the structure of a parseRequest.object different from a newly created ParseObject ( ie Test() ) ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants