# 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}/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