Browse Source

feat(polls): Allow moderators to draft polls

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/13506/head
Joas Schilling 1 year ago
parent
commit
7adfeb2218
No known key found for this signature in database GPG Key ID: F72FA5B49FFA96B0
  1. 2
      appinfo/routes/routesPollController.php
  2. 1
      docs/capabilities.md
  3. 1
      lib/Capabilities.php
  4. 101
      lib/Controller/PollController.php
  5. 35
      lib/Federation/Proxy/TalkV1/Controller/PollController.php
  6. 1
      lib/Model/Poll.php
  7. 14
      lib/Model/PollMapper.php
  8. 13
      lib/Service/PollService.php
  9. 176
      openapi-full.json
  10. 176
      openapi.json
  11. 98
      src/types/openapi/openapi-full.ts
  12. 98
      src/types/openapi/openapi.ts
  13. 5
      tests/integration/features/bootstrap/FeatureContext.php
  14. 41
      tests/integration/features/chat-3/poll.feature

2
appinfo/routes/routesPollController.php

@ -21,6 +21,8 @@ return [
'ocs' => [
/** @see \OCA\Talk\Controller\PollController::createPoll() */
['name' => 'Poll#createPoll', 'url' => '/api/{apiVersion}/poll/{token}', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\PollController::getAllDraftPolls() */
['name' => 'Poll#getAllDraftPolls', 'url' => '/api/{apiVersion}/poll/{token}/drafts', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\PollController::showPoll() */
['name' => 'Poll#showPoll', 'url' => '/api/{apiVersion}/poll/{token}/{pollId}', 'verb' => 'GET', 'requirements' => $requirementsWithPollId],
/** @see \OCA\Talk\Controller\PollController::votePoll() */

1
docs/capabilities.md

@ -158,4 +158,5 @@
## 20.1
* `archived-conversations` (local) - Conversations can be marked as archived which will hide them from the conversation list by default
* `talk-polls-drafts` - Whether moderators can store and retrieve poll drafts
* `config => call => start-without-media` (local) - Boolean, whether media should be disabled when starting or joining a conversation

1
lib/Capabilities.php

@ -104,6 +104,7 @@ class Capabilities implements IPublicCapability {
'mention-permissions',
'edit-messages-note-to-self',
'archived-conversations',
'talk-polls-drafts',
];
public const LOCAL_FEATURES = [

101
lib/Controller/PollController.php

@ -14,6 +14,7 @@ use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Exceptions\WrongPermissionsException;
use OCA\Talk\Middleware\Attribute\FederationSupported;
use OCA\Talk\Middleware\Attribute\RequireModeratorOrNoLobby;
use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant;
use OCA\Talk\Middleware\Attribute\RequireParticipant;
use OCA\Talk\Middleware\Attribute\RequirePermission;
use OCA\Talk\Middleware\Attribute\RequireReadWriteConversation;
@ -58,6 +59,7 @@ class PollController extends AEnvironmentAwareController {
* @param 0|1 $resultMode Mode how the results will be shown
* @psalm-param Poll::MODE_* $resultMode Mode how the results will be shown
* @param int $maxVotes Number of maximum votes per voter
* @param bool $draft Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
* @return DataResponse<Http::STATUS_CREATED, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array<empty>, array{}>
*
* 201: Poll created successfully
@ -69,11 +71,11 @@ class PollController extends AEnvironmentAwareController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
public function createPoll(string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
public function createPoll(string $question, array $options, int $resultMode, int $maxVotes, bool $draft = false): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController::class);
return $proxy->createPoll($this->room, $this->participant, $question, $options, $resultMode, $maxVotes);
return $proxy->createPoll($this->room, $this->participant, $question, $options, $resultMode, $maxVotes, $draft);
}
if ($this->room->getType() !== Room::TYPE_GROUP
@ -81,6 +83,10 @@ class PollController extends AEnvironmentAwareController {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
if ($draft === true && !$this->participant->hasModeratorPermissions()) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$attendee = $this->participant->getAttendee();
try {
$poll = $this->pollService->createPoll(
@ -91,33 +97,66 @@ class PollController extends AEnvironmentAwareController {
$question,
$options,
$resultMode,
$maxVotes
$maxVotes,
$draft,
);
} catch (\Exception $e) {
$this->logger->error('Error creating poll', ['exception' => $e]);
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$message = json_encode([
'message' => 'object_shared',
'parameters' => [
'objectType' => 'talk-poll',
'objectId' => $poll->getId(),
'metaData' => [
'type' => 'talk-poll',
'id' => $poll->getId(),
'name' => $question,
]
],
], JSON_THROW_ON_ERROR);
if (!$draft) {
$message = json_encode([
'message' => 'object_shared',
'parameters' => [
'objectType' => 'talk-poll',
'objectId' => $poll->getId(),
'metaData' => [
'type' => 'talk-poll',
'id' => $poll->getId(),
'name' => $question,
]
],
], JSON_THROW_ON_ERROR);
try {
$this->chatManager->addSystemMessage($this->room, $attendee->getActorType(), $attendee->getActorId(), $message, $this->timeFactory->getDateTime(), true);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
try {
$this->chatManager->addSystemMessage($this->room, $attendee->getActorType(), $attendee->getActorId(), $message, $this->timeFactory->getDateTime(), true);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
}
}
return new DataResponse($this->renderPoll($poll, []), Http::STATUS_CREATED);
return new DataResponse($this->renderPoll($poll), Http::STATUS_CREATED);
}
/**
* Get all drafted polls
*
* Required capability: `talk-polls-drafts`
*
* @return DataResponse<Http::STATUS_OK, list<TalkPoll>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
*
* 200: Poll returned
* 403: User is not a moderator
* 404: Poll not found
*/
#[FederationSupported]
#[PublicPage]
#[RequireModeratorParticipant]
public function getAllDraftPolls(): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController::class);
return $proxy->getDraftsForRoom($this->room, $this->participant);
}
$polls = $this->pollService->getDraftsForRoom($this->room->getId());
$data = [];
foreach ($polls as $poll) {
$data[] = $this->renderPoll($poll);
}
return new DataResponse($data);
}
/**
@ -143,7 +182,11 @@ class PollController extends AEnvironmentAwareController {
try {
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
} catch (DoesNotExistException $e) {
} catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if ($poll->getStatus() === Poll::STATUS_DRAFT && !$this->participant->hasModeratorPermissions()) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
@ -181,7 +224,11 @@ class PollController extends AEnvironmentAwareController {
try {
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
} catch (\Exception $e) {
} catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if ($poll->getStatus() === Poll::STATUS_DRAFT) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
@ -222,9 +269,10 @@ class PollController extends AEnvironmentAwareController {
*
* @param int $pollId ID of the poll
* @psalm-param non-negative-int $pollId
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_ACCEPTED|Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
*
* 200: Poll closed successfully
* 202: Poll draft was deleted successfully
* 400: Poll already closed
* 403: Missing permissions to close poll
* 404: Poll not found
@ -242,10 +290,15 @@ class PollController extends AEnvironmentAwareController {
try {
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
} catch (\Exception $e) {
} catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if ($poll->getStatus() === Poll::STATUS_DRAFT) {
$this->pollService->deleteByPollId($poll->getId());
return new DataResponse([], Http::STATUS_ACCEPTED);
}
if ($poll->getStatus() === Poll::STATUS_CLOSED) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

35
lib/Federation/Proxy/TalkV1/Controller/PollController.php

@ -28,6 +28,38 @@ class PollController {
) {
}
/**
* @return DataResponse<Http::STATUS_OK, list<TalkPoll>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
* @throws CannotReachRemoteException
*
* 200: Polls returned
* 404: Polls not found
*
* @see \OCA\Talk\Controller\PollController::showPoll()
*/
public function getDraftsForRoom(Room $room, Participant $participant): DataResponse {
$proxy = $this->proxy->get(
$participant->getAttendee()->getInvitedCloudId(),
$participant->getAttendee()->getAccessToken(),
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/drafts',
);
$status = $proxy->getStatusCode();
if ($status === Http::STATUS_NOT_FOUND || $status === Http::STATUS_FORBIDDEN) {
return new DataResponse([], $status);
}
/** @var list<TalkPoll> $list */
$list = $this->proxy->getOCSData($proxy);
$data = [];
foreach ($list as $poll) {
$data[] = $this->userConverter->convertPoll($room, $poll);
}
return new DataResponse($data);
}
/**
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
* @throws CannotReachRemoteException
@ -101,7 +133,7 @@ class PollController {
*
* @see \OCA\Talk\Controller\PollController::createPoll()
*/
public function createPoll(Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
public function createPoll(Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes, bool $draft): DataResponse {
$proxy = $this->proxy->post(
$participant->getAttendee()->getInvitedCloudId(),
$participant->getAttendee()->getAccessToken(),
@ -111,6 +143,7 @@ class PollController {
'options' => $options,
'resultMode' => $resultMode,
'maxVotes' => $maxVotes,
'draft' => $draft,
],
);

1
lib/Model/Poll.php

@ -41,6 +41,7 @@ use OCP\AppFramework\Db\Entity;
class Poll extends Entity {
public const STATUS_OPEN = 0;
public const STATUS_CLOSED = 1;
public const STATUS_DRAFT = 2;
public const MODE_PUBLIC = 0;
public const MODE_HIDDEN = 1;
public const MAX_VOTES_UNLIMITED = 0;

14
lib/Model/PollMapper.php

@ -27,6 +27,20 @@ class PollMapper extends QBMapper {
parent::__construct($db, 'talk_polls', Poll::class);
}
/**
* @return Poll[]
*/
public function getDraftsByRoomId(int $roomId): array {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from($this->getTableName())
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('status', $query->createNamedParameter(Poll::STATUS_DRAFT, IQueryBuilder::PARAM_INT)));
return $this->findEntities($query);
}
/**
* @param int $pollId
* @return Poll

13
lib/Service/PollService.php

@ -29,7 +29,7 @@ class PollService {
) {
}
public function createPoll(int $roomId, string $actorType, string $actorId, string $displayName, string $question, array $options, int $resultMode, int $maxVotes): Poll {
public function createPoll(int $roomId, string $actorType, string $actorId, string $displayName, string $question, array $options, int $resultMode, int $maxVotes, bool $draft): Poll {
$question = trim($question);
if ($question === '' || strlen($question) > 32_000) {
@ -78,12 +78,23 @@ class PollService {
$poll->setVotes(json_encode([]));
$poll->setResultMode($resultMode);
$poll->setMaxVotes($maxVotes);
if ($draft) {
$poll->setStatus(Poll::STATUS_DRAFT);
}
$this->pollMapper->insert($poll);
return $poll;
}
/**
* @param int $roomId
* @return Poll[]
*/
public function getDraftsForRoom(int $roomId): array {
return $this->pollMapper->getDraftsByRoomId($roomId);
}
/**
* @param int $roomId
* @param int $pollId

176
openapi-full.json

@ -8724,6 +8724,11 @@
"type": "integer",
"format": "int64",
"description": "Number of maximum votes per voter"
},
"draft": {
"type": "boolean",
"default": false,
"description": "Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)"
}
}
}
@ -8825,6 +8830,149 @@
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
"get": {
"operationId": "poll-get-all-draft-polls",
"summary": "Get all drafted polls",
"description": "Required capability: `talk-polls-drafts`",
"tags": [
"poll"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "apiVersion",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"v1"
],
"default": "v1"
}
},
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string",
"pattern": "^[a-z0-9]{4,30}$"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Poll returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Poll"
}
}
}
}
}
}
}
}
},
"403": {
"description": "User is not a moderator",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"404": {
"description": "Poll not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
"get": {
"operationId": "poll-show-poll",
@ -9205,6 +9353,34 @@
}
}
},
"202": {
"description": "Poll draft was deleted successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"400": {
"description": "Poll already closed",
"content": {

176
openapi.json

@ -8611,6 +8611,11 @@
"type": "integer",
"format": "int64",
"description": "Number of maximum votes per voter"
},
"draft": {
"type": "boolean",
"default": false,
"description": "Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)"
}
}
}
@ -8712,6 +8717,149 @@
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
"get": {
"operationId": "poll-get-all-draft-polls",
"summary": "Get all drafted polls",
"description": "Required capability: `talk-polls-drafts`",
"tags": [
"poll"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "apiVersion",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"v1"
],
"default": "v1"
}
},
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string",
"pattern": "^[a-z0-9]{4,30}$"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Poll returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Poll"
}
}
}
}
}
}
}
}
},
"403": {
"description": "User is not a moderator",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"404": {
"description": "Poll not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
"get": {
"operationId": "poll-show-poll",
@ -9092,6 +9240,34 @@
}
}
},
"202": {
"description": "Poll draft was deleted successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"400": {
"description": "Poll already closed",
"content": {

98
src/types/openapi/openapi-full.ts

@ -583,6 +583,26 @@ export type paths = {
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get all drafted polls
* @description Required capability: `talk-polls-drafts`
*/
get: operations["poll-get-all-draft-polls"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
parameters: {
query?: never;
@ -5112,6 +5132,11 @@ export interface operations {
* @description Number of maximum votes per voter
*/
maxVotes: number;
/**
* @description Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
* @default false
*/
draft?: boolean;
};
};
};
@ -5146,6 +5171,65 @@ export interface operations {
};
};
};
"poll-get-all-draft-polls": {
parameters: {
query?: never;
header: {
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};
path: {
apiVersion: "v1";
token: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Poll returned */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: components["schemas"]["Poll"][];
};
};
};
};
/** @description User is not a moderator */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
/** @description Poll not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
};
};
"poll-show-poll": {
parameters: {
query?: never;
@ -5295,6 +5379,20 @@ export interface operations {
};
};
};
/** @description Poll draft was deleted successfully */
202: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
/** @description Poll already closed */
400: {
headers: {

98
src/types/openapi/openapi.ts

@ -583,6 +583,26 @@ export type paths = {
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get all drafted polls
* @description Required capability: `talk-polls-drafts`
*/
get: operations["poll-get-all-draft-polls"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
parameters: {
query?: never;
@ -4593,6 +4613,11 @@ export interface operations {
* @description Number of maximum votes per voter
*/
maxVotes: number;
/**
* @description Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
* @default false
*/
draft?: boolean;
};
};
};
@ -4627,6 +4652,65 @@ export interface operations {
};
};
};
"poll-get-all-draft-polls": {
parameters: {
query?: never;
header: {
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};
path: {
apiVersion: "v1";
token: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Poll returned */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: components["schemas"]["Poll"][];
};
};
};
};
/** @description User is not a moderator */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
/** @description Poll not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
};
};
"poll-show-poll": {
parameters: {
query?: never;
@ -4776,6 +4860,20 @@ export interface operations {
};
};
};
/** @description Poll draft was deleted successfully */
202: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: unknown;
};
};
};
};
/** @description Poll already closed */
400: {
headers: {

5
tests/integration/features/bootstrap/FeatureContext.php

@ -2407,6 +2407,9 @@ class FeatureContext implements Context, SnippetAcceptingContext {
if ($data['maxVotes'] === 'unlimited') {
$data['maxVotes'] = 0;
}
if (isset($data['draft'])) {
$data['draft'] = (bool)$data['draft'];
}
$this->setCurrentUser($user);
$this->sendRequest(
@ -2528,6 +2531,8 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$expected['status'] = 0;
} elseif ($expected['status'] === 'closed') {
$expected['status'] = 1;
} elseif ($expected['status'] === 'draft') {
$expected['status'] = 2;
}
if (str_ends_with($expected['actorId'], '@{$LOCAL_URL}')) {

41
tests/integration/features/chat-3/poll.feature

@ -805,3 +805,44 @@ Feature: chat-2/poll
Then user "participant1" sees the following system messages in room "room" with 200 (v1)
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
| room | users | participant1 | history_cleared | You cleared the history of the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
Scenario: Create a public poll without max votes limit
Given user "participant1" creates room "room" (v4)
| roomType | 2 |
| roomName | room |
When user "participant1" adds user "participant2" to room "room" with 200 (v4)
When user "participant1" creates a poll in room "room" with 201
| question | What is the question? |
| options | ["Where are you?","How much is the fish?"] |
| resultMode | public |
| maxVotes | unlimited |
| draft | 1 |
Then user "participant1" sees the following messages in room "room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters |
Then user "participant1" sees poll "What is the question?" in room "room" with 200
| id | POLL_ID(What is the question?) |
| question | What is the question? |
| options | ["Where are you?","How much is the fish?"] |
| votes | [] |
| numVoters | 0 |
| resultMode | public |
| maxVotes | unlimited |
| actorType | users |
| actorId | participant1 |
| actorDisplayName | participant1-displayname |
| status | draft |
| votedSelf | not voted |
Then user "participant2" sees poll "What is the question?" in room "room" with 404
Then user "participant1" votes for options "[1]" on poll "What is the question?" in room "room" with 404
Then user "participant2" votes for options "[1]" on poll "What is the question?" in room "room" with 404
Then user "participant1" closes poll "What is the question?" in room "room" with 202
Then user "participant1" sees poll "What is the question?" in room "room" with 404
Then user "participant2" sees poll "What is the question?" in room "room" with 404
Then user "participant1" sees the following system messages in room "room" with 200 (v1)
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
| room | users | participant1 | user_added | You added {user} | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
| room | users | participant1 | conversation_created | You created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
Then user "participant2" sees the following system messages in room "room" with 200 (v1)
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
| room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
| room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
Loading…
Cancel
Save