Hello,
I have the following service function:
/**
* Retrieves a specific test suite by its ID.
*
* @route GET /testsuites/{suiteId}
*/
async getTestSuite() {
const suiteId = this.request.pathParams.suiteId;
const whereClause = `suiteId = ${suiteId}`;
const queryBuilder =
Backendless.DataQueryBuilder.create().setWhereClause(whereClause);
return await Backendless.Data.of("testSuites").find(queryBuilder);
}
/**
* Updates an existing test suite in the database.
*
* @param {Object} testSuite - The object containing updated test suite data.
*
* @route PUT /testsuites/{suiteId}
*/
async updateTestSuite(testSuite) {
return await Backendless.Data.of("testSuites").save(
new TestSuite(
testSuite.testSuiteName,
await this.getTestSuite()[0].objectId
)
);
}
However, when I test out PUT /testsuites/{suiteId}
in the Backendless console, I get a 400 error even when a valid suite ID and test suite object is provided:
curl -X "PUT" "<my_api_url>/testsuites/1" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d $'{
"testSuiteName": "Test"
}'
{
"code": 0,
"message": "Cannot read properties of null (reading 'testSuiteName')",
"errorData": {}
}
When I try to remove the {suiteId}
path param from the PUT route, the API recognizes the request body. Is it not possible to have a path param and request body at the same time for PUT requests?
Hello @Vernon_Cenzon
Welcome to our community and thank you for trying out Backendless.
We will be happy to assist you. I need to ask you a few more questions so I can understand the problem better.
Is your application id 35914BA5-AEE4-E0BA-FF71-B4D4504FCB00
?
I did not find any service (Backendless console - API Services - Services) in your app, or do you mean other services (service function)?
How can I reproduce your problem, please provide the steps to reproduce.
Regards,
Volodymyr
Hi @Volodymyr_Ialovyi ! Thanks for responding.
Is your application id 35914BA5-AEE4-E0BA-FF71-B4D4504FCB00
?
Yes
I did not find any service (Backendless console - API Services - Services) in your app, or do you mean other services (service function)?
Sorry, yes I don’t have any API services deployed yet. I only test out in debug mode at the moment and that’s where I encountered the isssue.
How can I reproduce your problem, please provide the steps to reproduce.
- Configure a table named
testSuites
that has at least one column named suiteName
(but this might be optional because I don’t think the issue lies with the call to Backendless.Data.of("testSuites").save()
, but more on resolving testSuite.testSuiteName
when creating a new TestSuite
instance)
- Create a class for the
TestSuite
custom data type with at least a suiteName
property.
- Create a service class with at least the 2 functions
getTestSuite()
and updateTestSuite()
mentioned in my original post above.
- Run the API service in debug and make the PUT request with both a valid
suiteId
path param and testSuite
object in the request payload
These are the steps I did that led me to encountering the issue but perhaps the issue would also be encountered even just with a PUT request to a URL with a path param and request object payload at the same time?
Update:
I tried modifying updateTestSuite()
like this:
/**
* Updates an existing test suite in the database.
*
* @param {String} testSuiteName - The new name of the test suite.
* @param {String} [someOtherProperty] - Some other property.
*
* @route PUT /testsuites/{suiteId}
*/
async updateTestSuite(testSuiteName, someOtherProperty) {
// Retrieve test suite from the database based on {suiteId} path parameter to get the corresponding objectId
const testSuite = await this.getTestSuite();
return await Backendless.Data.of("testSuites").save(
new TestSuite(testSuiteName, testSuite[0].objectId)
);
}
where instead of accepting a testSuite
object containing a testSuiteName
property as the argument, it now accepts 2 separate arguments testSuiteName
and someOtherProperty
(so that Backendless will make the request body schema an object). All other logic remains the same.
When I tested it, it worked as I intended.
I understand that there are better ways to implement this function (i.e. just have testSuiteName
as the sole argument, or perhaps even directly include objectId
as an argument as well to skip having to look for it in the DB). It just seems weird to me that something like this would work:
/**
* @param {String} arg1
* @param {String} arg2
* @route PUT /path/{param}
*/
myFunction(arg1, arg2) {
return {"arg1": arg1, "arg2": arg2};
}
// curl -X "PUT" "path/1" \
// -H 'Content-Type: application/json' \
// -H 'Accept: application/json' \
// -d $'{"arg1": "hello", "arg2": "world"}'
// > {arg1: "hello", arg2: "world"}
while something like this won’t:
/**
* @param {Object} arg1
* @route PUT /path/{param}
*/
myFunction(arg1) {
return arg1;
}
// curl -X "PUT" "path/1" \
// -H 'Content-Type: application/json' \
// -H 'Accept: application/json' \
// -d $'{"someProperty": "hello", "otherProperty": "world"}'
// > null
or maybe I’m just missing something?
Hello @Vernon_Cenzon!
Please note that within the body, keys must be used exactly as described in the @param
annotation.
For instance, instead of using "testSuiteName": "Test"
, you should use "testSuite": "Test".
To illustrate with the last example you mentioned, the payload in the body should look like this:
{ "arg1": {"someProperty": "hello", "otherProperty": "world"} }
I hope this helps.
Regards,
Alexander