-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
Comments
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. |
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 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
so it does get called on .save() |
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 |
It's not empty though. They're already there and existing. |
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. |
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. |
I got it to save just now by changing 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 |
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 |
What happens when you try the following?
|
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 |
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 There are 1000+ examples in the current unit tests. Look for one that seems more intuitive, then go to more complex |
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. |
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
converts the Pointer to just an Object which is then what we pass to the ParseHookResponse at the end of the beforeSave
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 |
I think I may understand where the issue is at now, at this line: parse-server-swift/Sources/ParseServerSwift/Parse.swift Lines 49 to 50 in 12db874
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. |
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. |
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)
} |
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! |
@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 ! sorry I haven't checked github in a while. I'll update to the latest versions and test this out. thanks! |
I just updated to 0.12 and haven't totally tested it out with triggers. but, I do get this error: 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)) |
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! |
thanks I see it. I overwrote the Parse+Vapor.swift file accidentally. |
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 |
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. 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! |
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. |
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: parse-server-swift/Sources/ParseServerSwift/configure.swift Lines 18 to 19 in 336b0d2
See #74 and https://github.com/netreconlab/parse-server-swift/releases/tag/0.13.0 for fix of using your own routes. |
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. |
You probably just need to make your routes function
This was only needed if you are using version <0.13, if you are using >=0.13, you can pass any Routes method to parse-server-swift/Sources/App/entrypoint.swift Lines 31 to 34 in 7c11bd3
|
regarding dockerfile, should I be copying the one in ParseServerSwift package for my project or the default one by Vapor? |
I just tried this and it worked. I also needed to add import BusogCloud (my app). thanks! :) the busogRoutes function is being seen now. |
The docker file in |
Hi @cbaker6, unfortunately I'm still getting the same error: these screenshots are from the ParseDashboard error when just editing an existing data. Changing an Object's property to |
This is for running beforeSave trigger by the way. |
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.
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())
}
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:
and of course I can't just .toPointer() object to pass to ParseHookResponse. |
is the structure of a parseRequest.object different from a newly created ParseObject ( ie Test() ) ? |
When using beforeSave trigger on an object that has a pointer to another object like so:
Doing nothing but just having the beforeSave trigger on produces a schema mismatch error where it expects a Pointer to the sub-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.
The text was updated successfully, but these errors were encountered: