added allowance of empty payload for documentsdb

This commit is contained in:
ArnabChatterjee20k
2026-04-10 13:59:45 +05:30
parent 938e65cb02
commit d13dbae0fe
3 changed files with 109 additions and 7 deletions
@@ -50,6 +50,11 @@ class Create extends Action
return UtopiaResponse::MODEL_DOCUMENT_LIST;
}
protected function getSupportForEmptyDocument()
{
return false;
}
public function __construct()
{
$this
@@ -139,30 +144,42 @@ class Create extends Action
->inject('eventProcessor')
->callback($this->action(...));
}
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization, EventProcessor $eventProcessor): void
{
$data = \is_string($data)
? \json_decode($data, true)
: $data;
$supportsEmptyDocument = $this->getSupportForEmptyDocument();
$hasData = !empty($data);
$hasDocuments = !empty($documents);
/**
* Determine which internal path to call, single or bulk
*/
if (empty($data) && empty($documents)) {
if (!$supportsEmptyDocument && !$hasData && !$hasDocuments) {
// No single or bulk documents provided
throw new Exception($this->getMissingDataException());
}
if (!empty($data) && !empty($documents)) {
// When empty documents are supported, an empty payload should still be treated as single create.
if ($supportsEmptyDocument && !$hasData && !$hasDocuments) {
$data = [];
$hasData = true;
}
if ($hasData && $hasDocuments) {
// Both single and bulk documents provided
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'You can only send one of the following parameters: data, ' . $this->getSDKGroup());
}
if (!empty($data) && empty($documentId)) {
if ($hasData && empty($documentId)) {
// Single document provided without document ID
$document = $this->isCollectionsAPI() ? 'Document' : 'Row';
$message = "$document ID is required when creating a single " . strtolower($document) . '.';
throw new Exception($this->getMissingDataException(), $message);
}
if (!empty($documents) && !empty($documentId)) {
if ($hasDocuments && !empty($documentId)) {
// Bulk documents provided with document ID
$documentId = $this->isCollectionsAPI() ? 'documentId' : 'rowId';
throw new Exception(
@@ -170,13 +187,13 @@ class Create extends Action
"Param \"$documentId\" is not allowed when creating multiple " . $this->getSDKGroup() . ', set "$id" on each instead.'
);
}
if (!empty($documents) && !empty($permissions)) {
if ($hasDocuments && !empty($permissions)) {
// Bulk documents provided with permissions
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Param "permissions" is disallowed when creating multiple ' . $this->getSDKGroup() . ', set "$permissions" on each instead');
}
$isBulk = true;
if (!empty($data)) {
$isBulk = $hasDocuments;
if ($hasData) {
// Single document provided, convert to single item array
// But remember that it was single to respond with a single document
$isBulk = false;
@@ -34,6 +34,12 @@ class Create extends DocumentCreate
return UtopiaResponse::MODEL_DOCUMENT_LIST;
}
protected function getSupportForEmptyDocument()
{
return true;
}
public function __construct()
{
$this
@@ -11539,4 +11539,83 @@ trait DatabasesBase
$this->assertEquals('Product B', $rows['body'][$this->getRecordResource()][0]['name']);
$this->assertEquals(139.99, $rows['body'][$this->getRecordResource()][0]['price']);
}
public function testDocumentWithEmptyPaylod(): void
{
$data = $this->setupCollection();
$databaseId = $data['databaseId'];
$document = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $data['moviesId']), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
$this->getRecordIdParam() => ID::unique(),
'data' => [],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
if ($this->getSupportForAttributes()) {
$this->assertEquals(400, $document['headers']['status-code']);
} else {
$this->assertEquals(201, $document['headers']['status-code']);
$this->assertEquals($data['moviesId'], $document['body'][$this->getContainerIdResponseKey()]);
$this->assertArrayNotHasKey('$collection', $document['body']);
$this->assertEquals($databaseId, $document['body']['$databaseId']);
$this->assertTrue(array_key_exists('$sequence', $document['body']));
$this->assertIsString($document['body']['$sequence']);
$documentId = $document['body']['$id'];
$fetched = $this->client->call(
Client::METHOD_GET,
$this->getRecordUrl($databaseId, $data['moviesId'], $documentId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders())
);
$this->assertEquals(200, $fetched['headers']['status-code']);
$this->assertEqualsCanonicalizing([
'$id',
'$databaseId',
'$createdAt',
'$updatedAt',
'$permissions',
'$sequence',
$this->getContainerIdResponseKey(),
], \array_keys($fetched['body']));
$this->assertFalse(array_key_exists('$tenant', $fetched['body']));
$updated = $this->client->call(
Client::METHOD_PATCH,
$this->getRecordUrl($databaseId, $data['moviesId'], $documentId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'data' => [
'status' => 'draft',
],
]
);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals('draft', $updated['body']['status']);
$refetched = $this->client->call(
Client::METHOD_GET,
$this->getRecordUrl($databaseId, $data['moviesId'], $documentId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders())
);
$this->assertEquals(200, $refetched['headers']['status-code']);
$this->assertEquals('draft', $refetched['body']['status']);
}
}
}