discourse/spec/requests/api/topics_spec.rb
Gabriel Grubba b39c30045b
FEATURE: Add skip notification option to group invite to topic (#29741)
* FEATURE: Add skip notification option to group invite to topic

* DEV: rename `skip_notification` to `should_notify`

* DEV: update `should_notify` param to be default `true` in controllers

* DEV: update spec to use `greater than` instead of `equal to` to prevent flakiness

* Update app/controllers/topics_controller.rb

Co-authored-by: David Taylor <david@taylorhq.com>

* DEV: merged two `#invite_group` specs into one

* DEV: Added test case for `invite-group` in requests spec

---------

Co-authored-by: David Taylor <david@taylorhq.com>
2024-11-14 13:00:15 -03:00

1059 lines
35 KiB
Ruby

# frozen_string_literal: true
require "swagger_helper"
RSpec.describe "topics" do
let(:"Api-Key") { Fabricate(:api_key).key }
let(:"Api-Username") { "system" }
path "/t/{id}/posts.json" do
get "Get specific posts from a topic" do
tags "Topics"
operationId "getSpecificPostsFromTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :post_body,
in: :body,
schema: {
type: :object,
properties: {
"post_ids[]": {
type: :integer,
},
},
required: ["post_ids[]"],
}
produces "application/json"
response "200", "specific posts" do
schema type: :object,
properties: {
post_stream: {
type: :object,
properties: {
posts: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
name: {
type: %i[string null],
},
username: {
type: :string,
},
avatar_template: {
type: :string,
},
created_at: {
type: :string,
},
cooked: {
type: :string,
},
post_number: {
type: :integer,
},
post_type: {
type: :integer,
},
updated_at: {
type: :string,
},
reply_count: {
type: :integer,
},
reply_to_post_number: {
type: %i[string null],
},
quote_count: {
type: :integer,
},
incoming_link_count: {
type: :integer,
},
reads: {
type: :integer,
},
readers_count: {
type: :integer,
},
score: {
type: :number,
},
yours: {
type: :boolean,
},
topic_id: {
type: :integer,
},
topic_slug: {
type: :string,
},
display_username: {
type: %i[string null],
},
primary_group_name: {
type: %i[string null],
},
flair_name: {
type: %i[string null],
},
flair_url: {
type: %i[string null],
},
flair_bg_color: {
type: %i[string null],
},
flair_color: {
type: %i[string null],
},
version: {
type: :integer,
},
can_edit: {
type: :boolean,
},
can_delete: {
type: :boolean,
},
can_recover: {
type: :boolean,
},
can_wiki: {
type: :boolean,
},
read: {
type: :boolean,
},
user_title: {
type: %i[string null],
},
actions_summary: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
can_act: {
type: :boolean,
},
},
},
},
moderator: {
type: :boolean,
},
admin: {
type: :boolean,
},
staff: {
type: :boolean,
},
user_id: {
type: :integer,
},
hidden: {
type: :boolean,
},
trust_level: {
type: :integer,
},
deleted_at: {
type: %i[string null],
},
user_deleted: {
type: :boolean,
},
edit_reason: {
type: %i[string null],
},
can_view_edit_history: {
type: :boolean,
},
wiki: {
type: :boolean,
},
reviewable_id: {
type: :integer,
},
reviewable_score_count: {
type: :integer,
},
reviewable_score_pending_count: {
type: :integer,
},
},
},
},
},
},
id: {
type: :integer,
},
}
let(:post_body) { { "post_ids[]": 1 } }
let(:id) { Fabricate(:topic).id }
run_test!
end
end
end
path "/t/{id}.json" do
get "Get a single topic" do
tags "Topics"
operationId "getTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
expected_request_schema = nil
produces "application/json"
response "200", "specific posts" do
let(:id) { Fabricate(:topic).id }
expected_response_schema = load_spec_schema("topic_show_response")
schema expected_response_schema
it_behaves_like "a JSON endpoint", 200 do
let(:expected_response_schema) { expected_response_schema }
let(:expected_request_schema) { expected_request_schema }
end
end
end
delete "Remove a topic" do
tags "Topics"
operationId "removeTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
response "200", "specific posts" do
let!(:post) { Fabricate(:post) }
let(:id) { post.topic.id }
run_test!
end
end
end
path "/t/-/{id}.json" do
put "Update a topic" do
tags "Topics"
operationId "updateTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :post_body,
in: :body,
schema: {
type: :object,
properties: {
topic: {
type: :object,
properties: {
title: {
type: :string,
},
category_id: {
type: :integer,
},
},
},
},
}
produces "application/json"
response "200", "topic updated" do
schema type: :object,
properties: {
basic_topic: {
type: :object,
properties: {
id: {
type: :integer,
},
title: {
type: :string,
},
fancy_title: {
type: :string,
},
slug: {
type: :string,
},
posts_count: {
type: :integer,
},
},
},
}
let(:post_body) { { title: "New topic title" } }
let!(:post) { Fabricate(:post) }
let(:id) { post.topic.id }
run_test! do |response|
data = JSON.parse(response.body)
expect(data["basic_topic"]["title"]).to eq("New topic title")
end
end
end
end
path "/t/{id}/invite.json" do
post "Invite to topic" do
tags "Topics", "Invites"
operationId "inviteToTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
user: {
type: :string,
},
email: {
type: :string,
},
},
}
produces "application/json"
response "200", "topic updated" do
schema type: :object,
properties: {
user: {
type: :object,
properties: {
id: {
type: :integer,
},
username: {
type: :string,
},
name: {
type: :string,
},
avatar_template: {
type: :string,
},
},
},
}
let(:username) { Fabricate(:user).username }
let(:request_body) { { user: username } }
let(:id) { Fabricate(:private_message_topic).id }
run_test! do |response|
data = JSON.parse(response.body)
expect(data["user"]["username"]).to eq(username)
end
end
end
end
path "/t/{id}/invite-group.json" do
post "Invite group to topic" do
tags "Topics", "Invites"
operationId "inviteGroupToTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
group: {
type: :string,
description: "The name of the group to invite",
},
should_notify: {
type: :boolean,
description: "Whether to notify the group, it defaults to true",
},
},
}
produces "application/json"
response "200", "invites to a PM" do
schema type: :object,
properties: {
group: {
type: :object,
properties: {
id: {
type: :integer,
},
name: {
type: :string,
},
},
},
}
let!(:admins) { Group[:admins] }
let(:request_body) { { group: admins.name } }
let(:pm) { Fabricate(:private_message_topic) }
let(:id) { pm.id }
run_test! do |response|
data = JSON.parse(response.body)
expect(data["group"]["name"]).to eq(admins.name)
expect(pm.allowed_groups.first.id).to eq(admins.id)
end
end
end
end
path "/t/{id}/bookmark.json" do
put "Bookmark topic" do
tags "Topics"
operationId "bookmarkTopic"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
produces "application/json"
response "200", "topic updated" do
let!(:post) { Fabricate(:post) }
let(:id) { post.topic.id }
run_test!
end
end
end
path "/t/{id}/status.json" do
put "Update the status of a topic" do
tags "Topics"
operationId "updateTopicStatus"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
status: {
type: :string,
enum: %w[closed pinned pinned_globally archived visible],
},
enabled: {
type: :string,
enum: %w[true false],
},
until: {
type: :string,
description: "Only required for `pinned` and `pinned_globally`",
example: "2030-12-31",
},
},
required: %w[status enabled],
}
produces "application/json"
response "200", "topic updated" do
schema type: :object,
properties: {
success: {
type: :string,
example: "OK",
},
topic_status_update: {
type: %i[string null],
},
}
let(:request_body) { { status: "closed", enabled: "true" } }
let(:id) { Fabricate(:topic).id }
run_test!
end
end
end
path "/latest.json" do
get "Get the latest topics" do
tags "Topics"
operationId "listLatestTopics"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter(
name: :order,
in: :query,
type: :string,
description:
"Enum: `default`, `created`, `activity`, `views`, `posts`, `category`, `likes`, `op_likes`, `posters`",
)
parameter(
name: :ascending,
in: :query,
type: :string,
description: "Defaults to `desc`, add `ascending=true` to sort asc",
)
produces "application/json"
response "200", "topic updated" do
schema type: :object,
properties: {
users: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
username: {
type: :string,
},
name: {
type: %i[string null],
},
avatar_template: {
type: :string,
},
},
},
},
primary_groups: {
type: :array,
items: {
},
},
topic_list: {
type: :object,
properties: {
can_create_topic: {
type: :boolean,
},
draft: {
type: %i[string null],
},
draft_key: {
type: :string,
},
draft_sequence: {
type: :integer,
},
per_page: {
type: :integer,
},
topics: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
title: {
type: :string,
},
fancy_title: {
type: :string,
},
slug: {
type: :string,
},
posts_count: {
type: :integer,
},
reply_count: {
type: :integer,
},
highest_post_number: {
type: :integer,
},
image_url: {
type: :string,
},
created_at: {
type: :string,
},
last_posted_at: {
type: :string,
},
bumped: {
type: :boolean,
},
bumped_at: {
type: :string,
},
archetype: {
type: :string,
},
unseen: {
type: :boolean,
},
last_read_post_number: {
type: :integer,
},
unread_posts: {
type: :integer,
},
pinned: {
type: :boolean,
},
unpinned: {
type: %i[string null],
},
visible: {
type: :boolean,
},
closed: {
type: :boolean,
},
archived: {
type: :boolean,
},
notification_level: {
type: :integer,
},
bookmarked: {
type: :boolean,
},
liked: {
type: :boolean,
},
views: {
type: :integer,
},
like_count: {
type: :integer,
},
has_summary: {
type: :boolean,
},
last_poster_username: {
type: :string,
},
category_id: {
type: :integer,
},
op_like_count: {
type: :integer,
},
pinned_globally: {
type: :boolean,
},
featured_link: {
type: %i[string null],
},
posters: {
type: :array,
items: {
type: :object,
properties: {
extras: {
type: :string,
},
description: {
type: :string,
},
user_id: {
type: :integer,
},
primary_group_id: {
type: %i[integer null],
},
},
},
},
},
},
},
},
},
}
let(:order) { "default" }
let(:ascending) { "false" }
run_test!
end
end
end
path "/top.json" do
get "Get the top topics filtered by period" do
tags "Topics"
operationId "listTopTopics"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter(
name: :period,
in: :query,
type: :string,
description: "Enum: `all`, `yearly`, `quarterly`, `monthly`, `weekly`, `daily`",
)
produces "application/json"
response "200", "response" do
schema type: :object,
properties: {
users: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
username: {
type: :string,
},
name: {
type: :string,
},
avatar_template: {
type: :string,
},
},
},
},
primary_groups: {
type: :array,
items: {
},
},
topic_list: {
type: :object,
properties: {
can_create_topic: {
type: :boolean,
},
draft: {
type: %i[string null],
},
draft_key: {
type: :string,
},
draft_sequence: {
type: :integer,
},
for_period: {
type: :string,
},
per_page: {
type: :integer,
},
topics: {
type: :array,
items: {
type: :object,
properties: {
id: {
type: :integer,
},
title: {
type: :string,
},
fancy_title: {
type: :string,
},
slug: {
type: :string,
},
posts_count: {
type: :integer,
},
reply_count: {
type: :integer,
},
highest_post_number: {
type: :integer,
},
image_url: {
type: %i[string null],
},
created_at: {
type: :string,
},
last_posted_at: {
type: :string,
},
bumped: {
type: :boolean,
},
bumped_at: {
type: :string,
},
archetype: {
type: :string,
},
unseen: {
type: :boolean,
},
last_read_post_number: {
type: :integer,
},
unread_posts: {
type: :integer,
},
pinned: {
type: :boolean,
},
unpinned: {
type: :boolean,
},
visible: {
type: :boolean,
},
closed: {
type: :boolean,
},
archived: {
type: :boolean,
},
notification_level: {
type: :integer,
},
bookmarked: {
type: :boolean,
},
liked: {
type: :boolean,
},
views: {
type: :integer,
},
like_count: {
type: :integer,
},
has_summary: {
type: :boolean,
},
last_poster_username: {
type: :string,
},
category_id: {
type: :integer,
},
op_like_count: {
type: :integer,
},
pinned_globally: {
type: :boolean,
},
featured_link: {
type: %i[string null],
},
posters: {
type: :array,
items: {
type: :object,
properties: {
extras: {
type: %i[string null],
},
description: {
type: :string,
},
user_id: {
type: :integer,
},
primary_group_id: {
type: %i[integer null],
},
},
},
},
},
},
},
},
},
}
let(:period) { "all" }
run_test!
end
end
end
path "/t/{id}/notifications.json" do
post "Set notification level" do
tags "Topics"
operationId "setNotificationLevel"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
notification_level: {
type: :string,
enum: %w[0 1 2 3],
},
},
required: ["notification_level"],
}
produces "application/json"
response "200", "topic updated" do
schema type: :object, properties: { success: { type: :string, example: "OK" } }
let(:request_body) { { notification_level: "3" } }
let(:id) { Fabricate(:topic).id }
run_test!
end
end
end
path "/t/{id}/change-timestamp.json" do
put "Update topic timestamp" do
tags "Topics"
operationId "updateTopicTimestamp"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
timestamp: {
type: :string,
example: "1594291380",
},
},
required: ["timestamp"],
}
produces "application/json"
response "200", "topic updated" do
schema type: :object, properties: { success: { type: :string, example: "OK" } }
let(:request_body) { { timestamp: "1594291380" } }
let!(:post) { Fabricate(:post) }
let(:id) { post.topic.id }
run_test!
end
end
end
path "/t/{id}/timer.json" do
post "Create topic timer" do
tags "Topics"
operationId "createTopicTimer"
consumes "application/json"
parameter name: "Api-Key", in: :header, type: :string, required: true
parameter name: "Api-Username", in: :header, type: :string, required: true
parameter name: :id, in: :path, schema: { type: :string }
parameter name: :request_body,
in: :body,
schema: {
type: :object,
properties: {
time: {
type: :string,
example: "",
},
status_type: {
type: :string,
},
based_on_last_post: {
type: :boolean,
},
category_id: {
type: :integer,
},
},
}
produces "application/json"
response "200", "topic updated" do
schema type: :object,
properties: {
success: {
type: :string,
example: "OK",
},
execute_at: {
type: :string,
},
duration: {
type: %i[string null],
},
based_on_last_post: {
type: :boolean,
},
closed: {
type: :boolean,
},
category_id: {
type: %i[integer null],
},
}
let(:request_body) { { time: Time.current + 1.day, status_type: "close" } }
let!(:topic_post) { Fabricate(:post) }
let(:id) { topic_post.topic.id }
run_test!
end
end
end
path "/t/external_id/{external_id}.json" do
get "Get topic by external_id" do
tags "Topics"
operationId "getTopicByExternalId"
consumes "application/json"
parameter name: :external_id, in: :path, type: :string, required: true
expected_request_schema = nil
produces "application/json"
response "301", "redirects to /t/{topic_id}.json" do
expected_response_schema = nil
schema expected_response_schema
let(:topic) { Fabricate(:topic, external_id: "external_id_1") }
let(:external_id) { topic.external_id }
run_test! { |response| expect(response).to redirect_to(topic.relative_url + ".json") }
end
end
end
end