Slim Application Error with API submissions endpoint OJS 3.4

Hello,

we are trying to streamline some internal processes, for this the we would need to be able to create new submissions programmatically

To test this, I am running OJS using docker, version ‘stable-3_4_0’

Here is my request:

curl http://...:8081/test/api/v1/submissions
-H ‘Content-Type: application/json’
-H ‘Accept: application/json’
-H ‘Authorization: Bearer **************’
-d ‘{“commentsForTheEditors”: “Hello”}’
-d ‘{“locale”: “en_US”}’
-d ‘{“sectionId”: 0}’
-d ‘{“userGroupId”: 0}’

I get a ‘Slim Application Error’.

Not much to work with. I copy the Apache2 error log at the end of this post. It does not help me much :frowning:

Am I doing something wrong ?

Best,
C.

[Mon Oct 16 19:58:26.901598 2023] [php:notice] [pid 23] [client ...:50420] Slim Application Error:
Type: TypeError
Message: PKP\API\v1\submissions\PKPSubmissionHandler::getWriteDisabledErrors(): Argument #2 ($params) must be of type array, null given, called in /var/www/html/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php on line 468
File: /var/www/html/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php
Line: 1650
Trace: #0 /var/www/html/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php(468): PKP\API\v1\submissions\PKPSubmissionHandler->getWriteDisabledErrors()
#1 [internal function]: PKP\API\v1\submissions\PKPSubmissionHandler->add()
#2 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php(40): call_user_func()
#3 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/Route.php(281): Slim\Handlers\Strategies\RequestResponse->__invoke()
#4 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(117): Slim\Route->__invoke()
#5 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/Route.php(268): Slim\Route->callMiddlewareStack()
#6 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/App.php(503): Slim\Route->run()
#7 /var/www/html/lib/pkp/classes/security/authorization/internal/ApiAuthorizationMiddleware.php(90): Slim\App->__invoke()
#8 [internal function]: PKP\security\authorization\internal\ApiAuthorizationMiddleware->__invoke()
#9 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#10 [internal function]: Slim\DeferredCallable->__invoke()
#11 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#12 /var/www/html/lib/pkp/classes/security/authorization/internal/ApiCsrfMiddleware.php(56): Slim\App->Slim\{closure}()
#13 [internal function]: PKP\security\authorization\internal\ApiCsrfMiddleware->__invoke()
#14 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#15 [internal function]: Slim\DeferredCallable->__invoke()
#16 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#17 /var/www/html/lib/pkp/classes/security/authorization/internal/ApiTokenDecodingMiddleware.php(140): Slim\App->Slim\{closure}()
#18 [internal function]: PKP\security\authorization\internal\ApiTokenDecodingMiddleware->__invoke()
#19 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#20 [internal function]: Slim\DeferredCallable->__invoke()
#21 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#22 /var/www/html/lib/pkp/classes/handler/APIHandler.php(83): Slim\App->Slim\{closure}()
#23 [internal function]: PKP\handler\APIHandler->PKP\handler\{closure}()
#24 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#25 [internal function]: Slim\DeferredCallable->__invoke()
#26 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#27 /var/www/html/lib/pkp/classes/handler/APIHandler.php(100): Slim\App->Slim\{closure}()
#28 [internal function]: PKP\handler\APIHandler->PKP\handler\{closure}()
#29 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#30 [internal function]: Slim\DeferredCallable->__invoke()
#31 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#32 /var/www/html/lib/pkp/classes/handler/APIHandler.php(105): Slim\App->Slim\{closure}()
#33 [internal function]: PKP\handler\APIHandler->PKP\handler\{closure}()
#34 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/DeferredCallable.php(57): call_user_func_array()
#35 [internal function]: Slim\DeferredCallable->__invoke()
#36 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(70): call_user_func()
#37 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(117): Slim\App->Slim\{closure}()
#38 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/App.php(392): Slim\App->callMiddlewareStack()
#39 /var/www/html/lib/pkp/lib/vendor/slim/slim/Slim/App.php(297): Slim\App->process()
#40 /var/www/html/lib/pkp/classes/core/APIRouter.php(110): Slim\App->run()
#41 /var/www/html/lib/pkp/classes/core/Dispatcher.php(165): PKP\core\APIRouter->route()
#42 /var/www/html/lib/pkp/classes/core/PKPApplication.php(387): PKP\core\Dispatcher->dispatch()
#43 /var/www/html/index.php(21): PKP\core\PKPApplication->execute()
#44 {main}
View in rendered output by enabling the “displayErrorDetails” setting.

Hi, answer my own post for updates.

So, my understanding so far it that the API is simply insufficiently documented to be usable and the error is due to an improper request.

Reverting to 3.3.0, for which the API documentation seems a bit more complete, I can now post a submission that results in a <200>. However, again the documentation is apparently insufficient and I can only try to guess the fields definitions by pulling out manually created submissions and adapting them for the POST request.

Still. Submissions are created but not visible through the online GUI. In the example below, one manually created submission is visible, three submissions created with the API are invisible and inaccessible (the only trace of them is the number “4” next to the “Unassigned” tab)

So I would call it progress, but I am still stuck. I assume that either I am missing some necessary fields to make the new submission valid, but I now also wonder if this API is operational at all.

In case, is the new POST request data:

new_submission =  {'_href': 'https://*******/index.php/datasets/api/v1/submissions/{new_submission_id}', 
 'contextId': 1,                             
 'currentPublicationId': {new_submission_id},  
 'dateLastActivity': None,                   
 'dateSubmitted': None,                      
 'id': {new_submission_id},                    
 'lastModified': None,                       
 'locale': 'en_US',                          
 'publications': [{'_href': f'https://*******/index.php/datasets/api/v1/submissions/{new_submission_id}/publications/{new_submission_id}',
   'authorsString': 'test test',  
   'authorsStringShort': 'test',        
   'categoryIds': [],                         
   'coverImage': {'en_US': None},             
   'datePublished': None,                     
   'fullTitle': {'en_US': 'The test title'},
   'galleys': [],                             
   'id': {new_submission_id},                 
   'locale': 'en_US',                         
   'pages': None,                             
   'prefix': {'en_US': ''},                   
   'primaryContactId': 1,                     
   'pub-id::publisher-id': None,              
   'sectionId': 1,                            
   'status': 1,                               
   'submissionId': {new_submission_id},       
   'subtitle': {'en_US': ''},
   'title': {'en_US': 'The test title'},
   'urlPublished': f'https://*******/index.php/datasets/article/view/{new_submission_id}/version/{new_submission_id}',
   'version': 1}],                            
 'stageId': 1,                                
 'status': 1,                                 
 'statusLabel': 'Queued',                     
 'submissionProgress': 1,                     
 'urlAuthorWorkflow': f'https://*******/index.php/datasets/authorDashboard/submission/{new_submission_id}',
 'urlEditorialWorkflow': f'https://*******/index.php/datasets/workflow/access/{new_submission_id}',
 'urlPublished': f'https://*******/index.php/datasets/article/view/{new_submission_id}',
 'urlWorkflow': f'https://*******/index.php/datasets/workflow/access/{new_submission_id}'}

C

Hi @Clement_Schneider.

Thanks for working on that and for the commitment you show on the details needed for your installation to work with the API calls.

You are currently working on the, probably, most demanding entity of the OJS universe, the Submission which, more or less, incorporates/includes a massive amount of all the other entities available.

As for the specific problem you encounter, I see in your initial comment that you are using a post that may contain data that won’t pass the validation process of the submission:

curl http://...:8081/test/api/v1/submissions
-H ‘Content-Type: application/json’
-H ‘Accept: application/json’
-H ‘Authorization: Bearer **************’
-d ‘{“commentsForTheEditors”: “Hello”}’
-d ‘{“locale”: “en_US”}’
-d ‘{“sectionId”: 0}’
-d ‘{“userGroupId”: 0}’
  1. en_US is not supported as a Locale in 3.4 - please use en
  2. You are trying to pass a sectionId that is not available in your installation.
  3. Same with the userGroupId

The reason you are getting a Slim error, and not the anticipated validation error response, is that the code can’t find the sectionId, resulting a Fatal Error in the server side. I have created a new bug issue for this.

Generally, when using ids of entities with the API, please be mindful, as it is common for non existing related entities to produce validation errors.

Finally, you will need more than one consecutive API calls in order to successfully create a new Submission with related data.

For example (following 3.4 API Documentation):

  1. To create a submission → documentation
  2. Edit the publication returned at the currentPublicationId attribute of the Submission you created at the first call → documentation
  3. Create a contributor for that publication → documentation

The way that those API requests are going to be called depends on your UI.

If you need more info on that, or you are finding inconsistencies regarding the API Documentation and the use of the API on your installation, please feel free to update this thread.

Hi @Dimitris_Efstathiou

I appreciate the answer, thank you.
I am unfortunately unable to translate it into any progress on my side. Let’s focus on the first issue, the first API call. You refer me to the documentation, which does not give me much than this example post data:

{ "commentsForTheEditors": "string",
    "locale": "string",
    "sectionId": 0,
    "userGroupId": 0}

It also states that commentsForTheEditors and userGroupId are optional. No clues are given about what values are acceptable for locale and sectionId. I assumed that “0” was the id of the default section. Actually, it seems that this id should be “1” (I confirmed it by manually submitting something, then pulling this submission using the api). Locale could be “en”, according to you.
So I tried with the following data:
‘{“locale”: “en”, “sectionId”: 1 }’ → Slim Application Error.
then I tried:
‘{“commentsForTheEditors”: “string”, “locale”: “en”, “sectionId”: 1 }’ → Slim Application Error.
then again:
‘{“commentsForTheEditors”: “string”, “locale”: “en”, “sectionId”: 1, “userGroupId”: 1 }’ → Slim Application Error.

The way that those API requests are going to be called depends on your UI.

I can use curl, python or anything. But I cannot guess the api expected values. Would it be possible to have a working example for this first call ?

Best,
Clem

@Clement_Schneider, thanks for replying and for the detailed answer.

I think that the problem in your case is some wrong id, perhaps with the sectionId. I tried to point out in my answer

Generally, when using ids of entities with the API, please be mindful, as it is common for non existing related entities to produce validation errors.

that working with ids during the API calls can cause validation errors.

Nevertheless, the API documentation can’t predict the “correct” ids of, let’s say, the sectionId, as this is totally up to the individual installation - In other words API documentation can’t be sure of the existing sectionIDs residing in your installation (sectionId being an example - that is true for every id).

This is true for every given API attribute. For example I proposed to change en_US to en, as an example, as I have no way to know what are the supported locales in your installation.

The API, on the other hand, should give a validation error response if something goes wrong with the inserted data. In the case of the add submission endpoint, the development team detected a problem handling a non existing sectionId.

I pointed that out here

The reason you are getting a Slim error, and not the anticipated validation error response, is that the code can’t find the sectionId , resulting a Fatal Error in the server side. I have created a new bug issue for this.

The issue has already a proposed fix that you are welcome to apply and test on your installation, in order to check if you will get a better validation error. Please check that you are working on the correct version of the software for this fix. What version of OJS are you working on right now?

Also, you could check the PHP errors of your installation to see if there is any Fatal Error, to better narrow down what the problem would be in your particular case.

We will be happy to have a feedback from you on how that went.

But but but, this api call requires only two mandatory parameters.

sectionId. The documentation states this should be an integer and provides 0 as an example. I have a fresh install of OJS 3_4_0-3 with a single Section “Articles”. As mentionned, I created a sumbission manually and requested the details with a GET request. Its sectionId was 1. I created a new section and manually submitted something to it. I found out that this sectionId was 2. At this point, is there any reason to suggest that 1 and 2 are non existant sectionId ? If yes, I need more instructions to find out what are valid ids.

I have no way to know what are the supported locales in your installation.

I have installed OJS with support for english. When pulling out the data of the manual submission, all fields involving locale are “en”. Is there any reason to suggest that “en” is not a valid locale keyword for my installation ? If yes, I need more instructions to find out the correct keyword.

I am grateful that you already implemented a fix to help debbuging, and really I would like to help. But I am only an occasional programmer and now I am a bit lost in git in order to find out how to grab this specific commit and apply it to my version. I cloned the PKP-LIB repo (GitHub - pkp/pkp-lib: The library used by PKP's applications OJS, OMP and OPS, open source software for scholarly publishing.) and tried to checkout to commit 7bf0bd9, that you pointed to me. It says that “error: pathspec ‘7bf0bd9’ did not match any file(s) known to git”. I am far from my initial problem.

Wouldn’t it be more efficient for the development team to replicate the problem, in order to tackle it ?
Here are now my last ‘test’ conditions:

Install OJS 3_4_0-3 with php8.1 and MariaDB. Tick English as locale.
Run the following request (here with Python).

import requests
url = 'http://localhost:8000/index.php/test/api/v1/submissions'
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer **************'}

data={"commentsForTheEditors": "Hello editors",
      "locale": "en",
      "sectionId": 1}
res = requests.post(url=url, headers=headers, data=data)

res is error 500 “Slim Application Error”.
In the log, this is what seems to be the error:

Type: TypeError
Message: PKP\API\v1\submissions\PKPSubmissionHandler::getWriteDisabledErrors(): Argument #2 ($params) must be of type array, null given, called in /~/ojs-3.4.0-3/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php on line 468
File: ~/ojs-3.4.0-3/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php
Line: 1650
Trace: #0 ~/ojs-3.4.0-3/lib/pkp/api/v1/submissions/PKPSubmissionHandler.php(468): >PKP\API\v1\submissions\PKPSubmissionHandler->getWriteDisabledErrors()

Best regards,
Clem

Hi @Clement_Schneider,

Can you please provide the way that you are setting up your installation? Are there any other data in, other than the initial OJS setup data? What are the exact steps that you are following before you make the API call that you are describing?

If the error you provided is actually the error that causes the 500 HTTP error, it could probably be that the API call is not properly formed, causing the $slimRequest->getParsedBody() at the $params = $this->convertStringsToSchema(PKPSchemaService::SCHEMA_SUBMISSION, $slimRequest->getParsedBody()); line of code to be null.

Even though I don’t want to get into the caller code specifics, in order to see if that is the problem I have these notes: I can see that you are using this code:

import requests
url = 'http://localhost:8000/index.php/test/api/v1/submissions'
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer **************'}

data={"commentsForTheEditors": "Hello editors",
      "locale": "en",
      "sectionId": 1}
res = requests.post(url=url, headers=headers, data=data)

Could you check if the $slimRequest->getParsedBody() actually returns any array of values, and if not, could you change your call to

res = requests.post(url=url, headers=headers, json=data)

instead of

res = requests.post(url=url, headers=headers, data=data)

Also using the curl toolset, do something like that

curl -X POST http://localhost:8000/index.php/test/api/v1/submissions \
     -H "Authorization: Bearer **************" \
     -H "Content-Type: application/json" \
     -d '{"commentsForTheEditors": "Hello editors", "locale": "en", "sectionId": 1}'

Finally, in your first comment I see that you are using curl toolset to make the call and in your last one python code. I suggest, in order to get to the bottom of the API issue specifically, to use one approach, the same throughout your tests, in order to avoid mixing possible problems, like, for example, having a wrong sectionId=0 in your first attempt, and a caller toolset problem/misuse in the second attempt.

Thanks.

Hello,

you were correct, the python request should have been

requests.post(url=url, headers=headers, json=data)

It now works, as well as with your curl command.
And indeed, I initially tried the python alternatively with the json or data attribute, but the wrong ids were the problem. Last night, I did not thought of switching the attribute back to json.

I appreciate the time you spent to help me. Thank you very much, I think I will be able to proceed now.

Cheers,
Clement.

1 Like