Repository: appwrite/appwrite
File: src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php (PHP)

Code before:
            name: 'createFileToken',
            description: <<<EOT
            Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.
            EOT,
            auth: [AuthType::ADMIN, AuthType::KEY],
            responses: [
                new SDKResponse(
                    code: Response::STATUS_CODE_CREATED,
                    model: Response::MODEL_RESOURCE_TOKEN,
                )
            ],
            contentType: ContentType::JSON
        ))
        ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
        ->param('fileId', '', new UID(), 'File unique ID.')
        ->param('expire', null, new Text(100), 'Token expiry date', true)
        ->inject('response')
        ->inject('dbForProject')
        ->inject('queueForEvents')
        ->callback($this->action(...));
    }

    public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
    {
        if (!is_null($expire)) {
            $validator = new DatetimeValidator(requireDateInFuture: true, precision: DateTimeValidator::PRECISION_DAYS, offset: 0);
            if (!$validator->isValid($expire)) {
                throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Token expiry date must be a valid date, and at least 1 day from now');
            }
        }


        /**
         * @var Document $bucket
         * @var Document $file
         */
        ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId);

        $fileSecurity = $bucket->getAttribute('fileSecurity', false);
        $validator = new Authorization(Database::PERMISSION_UPDATE);
        $bucketPermission = $validator->isValid($bucket->getUpdate());

        if ($fileSecurity) {
            $filePermission = $validator->isValid($file->getUpdate());
            if (!$bucketPermission && !$filePermission) {
                throw new Exception(Exception::USER_UNAUTHORIZED);
            }
        } elseif (!$bucketPermission) {
            throw new Exception(Exception::USER_UNAUTHORIZED);
        }


Review comment (suggestion):
This is not in param directly, to allow us to change exception message.
But can we make validaiton description nicer, so we dont need this?

There are more endpoitns that would benefit from nicer error message.

Code after:
            name: 'createFileToken',
            description: <<<EOT
            Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.
            EOT,
            auth: [AuthType::ADMIN, AuthType::KEY],
            responses: [
                new SDKResponse(
                    code: Response::STATUS_CODE_CREATED,
                    model: Response::MODEL_RESOURCE_TOKEN,
                )
            ],
            contentType: ContentType::JSON
        ))
        ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
        ->param('fileId', '', new UID(), 'File unique ID.')
        ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Token expiry date', true)
        ->inject('response')
        ->inject('dbForProject')
        ->inject('queueForEvents')
        ->callback($this->action(...));
    }

    public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
    {
        /**
         * @var Document $bucket
         * @var Document $file
         */
        ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId);

        $fileSecurity = $bucket->getAttribute('fileSecurity', false);
        $validator = new Authorization(Database::PERMISSION_UPDATE);
        $bucketPermission = $validator->isValid($bucket->getUpdate());

        if ($fileSecurity) {
            $filePermission = $validator->isValid($file->getUpdate());
            if (!$bucketPermission && !$filePermission) {
                throw new Exception(Exception::USER_UNAUTHORIZED);
            }
        } elseif (!$bucketPermission) {
            throw new Exception(Exception::USER_UNAUTHORIZED);
        }

        $token = $dbForProject->createDocument('resourceTokens', new Document([
            '$id' => ID::unique(),
            'secret' => Auth::tokenGenerator(128),
            'resourceId' => $bucketId . ':' . $fileId,
            'resourceInternalId' => $bucket->getSequence() . ':' . $file->getSequence(),
            'resourceType' => TOKENS_RESOURCE_TYPE_FILES,
            'expire' => $expire,
        ]));