From ab919332dc0c1cc3edcb5190f1f57f621364624e Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Mon, 11 May 2020 13:06:49 -0600 Subject: [PATCH] DEV: api documentation updates (#9612) * DEV: api documentation updates - Created a script to convert json responses to rswag - Documented several api endpoints - Switched rswag to use header based auth * Update script, fix some schema missmatches --- script/json_to_rswag.rb | 101 ++++++ spec/requests/api/categories_spec.rb | 360 +++++++++++++++++++- spec/requests/api/groups_spec.rb | 79 +++++ spec/requests/api/posts_spec.rb | 484 +++++++++++++++++++++++++++ spec/requests/api/users_spec.rb | 55 +++ 5 files changed, 1072 insertions(+), 7 deletions(-) create mode 100644 script/json_to_rswag.rb create mode 100644 spec/requests/api/groups_spec.rb create mode 100644 spec/requests/api/posts_spec.rb create mode 100644 spec/requests/api/users_spec.rb diff --git a/script/json_to_rswag.rb b/script/json_to_rswag.rb new file mode 100644 index 00000000000..e3e25ea2dc7 --- /dev/null +++ b/script/json_to_rswag.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true +require 'json' + +# This script is used for development to help document rswag api responses. It +# takes a json response string as an input and converts it to ruby in the format +# that rswag wants so that you can copy+paste it into the appropriate spec rather +# than typing it by hand. +# +# example: +# +# ruby script/json_to_rswag.rb '{"success":true,"active":true,"message":"Your account is activated and ready to use.","user_id":18}' +# +# will output: +# +# schema type: :object, properties: { +# success: { type: :boolean }, +# active: { type: :boolean }, +# message: { type: :string }, +# user_id: { type: :integer }, +# } + +class JsonToRswag + + def initialize + @output = Array.new + @output.push("schema type: :object, properties: {") + end + + def get_type(k, v) + type = "" + type = "integer" if v.is_a? Integer + type = "number" if v.is_a? Float + type = "object" if v.is_a? Hash + type = "array" if v.is_a? Array + type = "boolean" if v == true || v == false + type = "null" if v == nil + type = "string" if v.is_a? String + if type == "" + puts "Cannot determine type for:" + puts v + exit 1 + end + type + end + + def run(h, indent) + h.each do |k, v| + type = get_type(k, v) + + if type == "object" + @output << "#{' ' * indent}#{k}: {" + @output << "#{' ' * (indent + 2)}type: :object," + @output << "#{' ' * (indent + 2)}properties: {" + run(v, indent + 4) + @output << "#{' ' * indent}}," + end + + if type == "array" + @output << "#{' ' * indent}#{k}: {" + @output << "#{' ' * (indent + 2)}type: :array," + @output << "#{' ' * (indent + 2)}items: {" + a = v.first + if a.is_a? Hash + @output << "#{' ' * (indent + 4)}type: :object," + @output << "#{' ' * (indent + 4)}properties: {" + run(a , indent + 6) + @output << "#{' ' * (indent + 2)}}," + else + @output << "#{' ' * (indent + 2)}}," + end + @output << "#{' ' * indent}}," + end + + if type == "null" + @output << "#{' ' * indent}#{k}: { type: :string, nullable: true }," + elsif type != "object" && type != "array" + @output << "#{' ' * indent}#{k}: { type: :#{type} }," + end + end + + @output << "#{' ' * (indent - 2)}}" + end + + def print + puts @output + end + +end + +input = ARGV[0] + +if input == nil || input == "" + puts "Please pass in a json string." + exit 1 +end + +json = JSON.parse(input) + +json_to_rswag = JsonToRswag.new +json_to_rswag.run(json, 2) +json_to_rswag.print diff --git a/spec/requests/api/categories_spec.rb b/spec/requests/api/categories_spec.rb index 0c82cfa3892..5de0bdeee9a 100644 --- a/spec/requests/api/categories_spec.rb +++ b/spec/requests/api/categories_spec.rb @@ -4,6 +4,7 @@ require 'swagger_helper' describe 'categories' do let(:admin) { Fabricate(:admin) } + let!(:category) { Fabricate(:category, user: admin) } before do Jobs.run_immediately! @@ -13,11 +14,7 @@ describe 'categories' do path '/categories.json' do post 'Creates a category' do - before do - Jobs.run_immediately! - sign_in(admin) - end - tags 'Category' + tags 'Categories' consumes 'application/json' parameter name: :category, in: :body, schema: { type: :object, @@ -38,6 +35,46 @@ describe 'categories' do id: { type: :integer }, name: { type: :string }, color: { type: :string }, + text_color: { type: :string }, + slug: { type: :string }, + topic_count: { type: :integer }, + post_count: { type: :integer }, + position: { type: :integer }, + description: { type: :string, nullable: true }, + description_text: { type: :string, nullable: true }, + topic_url: { type: :string }, + read_restricted: { type: :boolean }, + permission: { type: :integer, nullable: true }, + notification_level: { type: :integer, nullable: true }, + can_edit: { type: :boolean }, + topic_template: { type: :string, nullable: true }, + has_children: { type: :boolean, nullable: true }, + sort_order: { type: :string, nullable: true }, + show_subcategory_list: { type: :boolean }, + num_featured_topics: { type: :integer }, + default_view: { type: :string, nullable: true }, + subcategory_list_style: { type: :string }, + default_topic_period: { type: :string }, + minimum_required_tags: { type: :integer }, + navigate_to_first_post_after_read: { type: :boolean }, + custom_fields: { type: :object }, + min_tags_from_required_group: { type: :integer }, + required_tag_group_name: { type: :string, nullable: true }, + available_groups: { type: :array }, + auto_close_hours: { type: :integer, nullable: true }, + auto_close_based_on_last_post: { type: :boolean }, + group_permissions: { type: :array }, + email_in: { type: :boolean, nullable: true }, + email_in_allow_strangers: { type: :boolean }, + mailinglist_mirror: { type: :boolean }, + all_topics_wiki: { type: :boolean }, + can_delete: { type: :boolean }, + cannot_delete_reason: { type: :string, nullable: true }, + allow_badges: { type: :boolean }, + topic_featured_link_allowed: { type: :boolean }, + search_priority: { type: :integer }, + uploaded_logo: { type: :string, nullable: true }, + uploaded_background: { type: :string, nullable: true }, }, required: ["id"] } @@ -49,7 +86,7 @@ describe 'categories' do end get 'Retreives a list of categories' do - tags 'Category' + tags 'Categories' produces 'application/json' response '200', 'categories response' do @@ -62,7 +99,46 @@ describe 'categories' do draft: { type: :string, nullable: true }, draft_key: { type: :string }, draft_sequence: { type: :integer }, - categories: { type: :array }, + categories: { + type: :array, + items: { + type: :object, + properties: { + id: { type: :integer }, + name: { type: :string }, + color: { type: :string }, + text_color: { type: :string }, + slug: { type: :string }, + topic_count: { type: :integer }, + post_count: { type: :integer }, + position: { type: :integer }, + description: { type: :string, nullable: true }, + description_text: { type: :string, nullable: true }, + topic_url: { type: :string, nullable: true }, + read_restricted: { type: :boolean }, + permission: { type: :integer, nullable: true }, + notification_level: { type: :integer, nullable: true }, + can_edit: { type: :boolean }, + topic_template: { type: :string, nullable: true }, + has_children: { type: :boolean, nullable: true }, + sort_order: { type: :string, nullable: true }, + show_subcategory_list: { type: :boolean }, + num_featured_topics: { type: :integer }, + default_view: { type: :string, nullable: true }, + subcategory_list_style: { type: :string }, + default_topic_period: { type: :string }, + minimum_required_tags: { type: :integer }, + navigate_to_first_post_after_read: { type: :boolean }, + topics_day: { type: :integer }, + topics_week: { type: :integer }, + topics_month: { type: :integer }, + topics_year: { type: :integer }, + topics_all_time: { type: :integer }, + uploaded_logo: { type: :string, nullable: true }, + uploaded_background: { type: :string, nullable: true }, + } + } + }, }, required: ["categories"] } }, required: ["category_list"] @@ -70,4 +146,274 @@ describe 'categories' do end end end + + path '/categories/{category_id}.json' do + + put 'Updates a category' do + tags 'Categories' + consumes 'application/json' + parameter name: :category_id, in: :path, schema: { type: :string } + parameter name: :category, in: :body, schema: { + type: :object, + properties: { + name: { type: :string }, + color: { type: :string }, + text_color: { type: :string }, + }, + required: [ 'name', 'color', 'text_color' ] + } + + produces 'application/json' + response '200', 'category created' do + schema type: :object, properties: { + success: { type: :string }, + category: { + type: :object, + properties: { + id: { type: :integer }, + name: { type: :string }, + color: { type: :string }, + text_color: { type: :string }, + slug: { type: :string }, + topic_count: { type: :integer }, + post_count: { type: :integer }, + position: { type: :integer }, + description: { type: :string, nullable: true }, + description_text: { type: :string, nullable: true }, + description_excerpt: { type: :string, nullable: true }, + topic_url: { type: :string, nullable: true }, + read_restricted: { type: :boolean }, + permission: { type: :string, nullable: true }, + notification_level: { type: :integer, nullable: true }, + can_edit: { type: :boolean }, + topic_template: { type: :string, nullable: true }, + has_children: { type: :string, nullable: true }, + sort_order: { type: :string, nullable: true }, + sort_ascending: { type: :string, nullable: true }, + show_subcategory_list: { type: :boolean }, + num_featured_topics: { type: :integer }, + default_view: { type: :string, nullable: true }, + subcategory_list_style: { type: :string }, + default_top_period: { type: :string }, + minimum_required_tags: { type: :integer }, + navigate_to_first_post_after_read: { type: :boolean }, + custom_fields: { + type: :object, + properties: { + } + }, + min_tags_from_required_group: { type: :integer }, + required_tag_group_name: { type: :string, nullable: true }, + available_groups: { + type: :array, + items: { + }, + }, + auto_close_hours: { type: :string, nullable: true }, + auto_close_based_on_last_post: { type: :boolean }, + group_permissions: { + type: :array, + items: { + type: :object, + properties: { + permission_type: { type: :integer }, + group_name: { type: :string }, + } + }, + }, + email_in: { type: :string, nullable: true }, + email_in_allow_strangers: { type: :boolean }, + mailinglist_mirror: { type: :boolean }, + all_topics_wiki: { type: :boolean }, + can_delete: { type: :boolean }, + cannot_delete_reason: { type: :string, nullable: true }, + allow_badges: { type: :boolean }, + topic_featured_link_allowed: { type: :boolean }, + search_priority: { type: :integer }, + uploaded_logo: { type: :string, nullable: true }, + uploaded_background: { type: :string, nullable: true }, + } + }, + } + + let(:category_id) { category.id } + run_test! + end + end + end + + path '/c/{category_id}.json' do + + get 'List topics' do + tags 'Categories' + produces 'application/json' + parameter name: :category_id, in: :path, schema: { type: :string } + + response '200', 'response' do + schema type: :object, properties: { + users: { + type: :array, + items: { + type: :object, + properties: { + id: { type: :integer }, + username: { type: :string }, + name: { type: :string, nullable: true }, + avatar_template: { type: :string }, + } + } + }, + primary_groups: { + type: :array + }, + topic_list: { + type: :object, + properties: { + can_create_topic: { type: :boolean }, + draft: { type: :string, nullable: true }, + 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, nullable: true }, + created_at: { type: :string }, + last_posted_at: { type: :string }, + bumped: { type: :boolean }, + bumped_at: { type: :string }, + archetype: { type: :string }, + unseen: { type: :boolean }, + pinned: { type: :boolean }, + unpinned: { type: :boolean, nullable: true }, + excerpt: { type: :string }, + visible: { type: :boolean }, + closed: { type: :boolean }, + archived: { type: :boolean }, + bookmarked: { type: :boolean, nullable: true }, + liked: { type: :boolean, nullable: true }, + 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: :string, nullable: true }, + posters: { + type: :array, + items: { + type: :object, + properties: { + extras: { type: :string }, + description: { type: :string }, + user_id: { type: :integer }, + primary_group_id: { type: :integer, nullable: true }, + } + } + } + } + } + } + } + } + } + let(:category_id) { category.id } + run_test! + end + end + end + + path '/c/{category_id}/show.json' do + + get 'Show category' do + tags 'Categories' + produces 'application/json' + parameter name: :category_id, in: :path, schema: { type: :string } + + response '200', 'response' do + schema type: :object, properties: { + category: { + type: :object, + properties: { + id: { type: :integer }, + name: { type: :string }, + color: { type: :string }, + text_color: { type: :string }, + slug: { type: :string }, + topic_count: { type: :integer }, + post_count: { type: :integer }, + position: { type: :integer }, + description: { type: :string, nullable: true }, + description_text: { type: :string, nullable: true }, + description_excerpt: { type: :string, nullable: true }, + topic_url: { type: :string, nullable: true }, + read_restricted: { type: :boolean }, + permission: { type: :integer }, + notification_level: { type: :integer, nullable: true }, + can_edit: { type: :boolean }, + topic_template: { type: :string, nullable: true }, + has_children: { type: :string, nullable: true }, + sort_order: { type: :string, nullable: true }, + sort_ascending: { type: :string, nullable: true }, + show_subcategory_list: { type: :boolean }, + num_featured_topics: { type: :integer }, + default_view: { type: :string, nullable: true }, + subcategory_list_style: { type: :string }, + default_top_period: { type: :string }, + minimum_required_tags: { type: :integer }, + navigate_to_first_post_after_read: { type: :boolean }, + custom_fields: { + type: :object, + properties: { + } + }, + min_tags_from_required_group: { type: :integer }, + required_tag_group_name: { type: :string, nullable: true }, + available_groups: { + type: :array, + items: { + }, + }, + auto_close_hours: { type: :string, nullable: true }, + auto_close_based_on_last_post: { type: :boolean }, + group_permissions: { + type: :array, + items: { + type: :object, + properties: { + permission_type: { type: :integer }, + group_name: { type: :string }, + } + }, + }, + email_in: { type: :string, nullable: true }, + email_in_allow_strangers: { type: :boolean }, + mailinglist_mirror: { type: :boolean }, + all_topics_wiki: { type: :boolean }, + can_delete: { type: :boolean }, + cannot_delete_reason: { type: :string, nullable: true }, + allow_badges: { type: :boolean }, + topic_featured_link_allowed: { type: :boolean }, + search_priority: { type: :integer }, + uploaded_logo: { type: :string, nullable: true }, + uploaded_background: { type: :string, nullable: true }, + } + }, + } + let(:category_id) { category.id } + run_test! + end + end + end + end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb new file mode 100644 index 00000000000..ab3112aa894 --- /dev/null +++ b/spec/requests/api/groups_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true +require 'swagger_helper' + +describe 'groups' do + + let(:admin) { Fabricate(:admin) } + + before do + Jobs.run_immediately! + sign_in(admin) + end + + path '/admin/groups.json' do + + post 'Creates a group' do + tags 'Group' + consumes 'application/json' + parameter name: :group, in: :body, schema: { + type: :object, + properties: { + group: { + type: :object, + properties: { + name: { type: :string }, + }, required: [ 'name' ] + } + }, + } + + produces 'application/json' + response '200', 'group created' do + schema type: :object, properties: { + basic_group: { + type: :object, + properties: { + id: { type: :integer }, + automatic: { type: :boolean }, + name: { type: :string }, + user_count: { type: :integer }, + mentionable_level: { type: :integer }, + messageable_level: { type: :integer }, + visibility_level: { type: :integer }, + automatic_membership_email_domains: { type: :string, nullable: true }, + automatic_membership_retroactive: { type: :boolean }, + primary_group: { type: :boolean }, + title: { type: :string, nullable: true }, + grant_trust_level: { type: :string, nullable: true }, + incoming_email: { type: :string, nullable: true }, + has_messages: { type: :boolean }, + flair_url: { type: :string, nullable: true }, + flair_bg_color: { type: :string, nullable: true }, + flair_color: { type: :string, nullable: true }, + bio_raw: { type: :string, nullable: true }, + bio_cooked: { type: :string, nullable: true }, + bio_excerpt: { type: :string, nullable: true }, + public_admission: { type: :boolean }, + public_exit: { type: :boolean }, + allow_membership_requests: { type: :boolean }, + full_name: { type: :string, nullable: true }, + default_notification_level: { type: :integer }, + membership_request_template: { type: :string, nullable: true }, + membership_visibility_level: { type: :integer }, + can_see_members: { type: :boolean }, + publish_read_state: { type: :boolean }, + }, + required: ["id"] + } + }, required: ["basic_group"] + + let(:group) { { name: 'awesome' } } + run_test! do |response| + data = JSON.parse(response.body) + expect(data['basic_group']['name']).to eq("awesome") + end + end + end + + end +end diff --git a/spec/requests/api/posts_spec.rb b/spec/requests/api/posts_spec.rb new file mode 100644 index 00000000000..d232cae1837 --- /dev/null +++ b/spec/requests/api/posts_spec.rb @@ -0,0 +1,484 @@ +# frozen_string_literal: true +require 'swagger_helper' + +describe 'posts' do + + let(:'Api-Key') { Fabricate(:api_key).key } + let(:'Api-Username') { 'system' } + + path '/posts.json' do + + get 'List latest posts across topics' do + tags 'Posts' + parameter name: 'Api-Key', in: :header, type: :string, required: true + parameter name: 'Api-Username', in: :header, type: :string, required: true + produces 'application/json' + + response '200', 'latest posts' do + schema type: :object, properties: { + latest_posts: { + type: :array, + items: { + type: :object, + properties: { + id: { type: :integer }, + name: { type: :string }, + 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: :string, nullable: true }, + 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 }, + topic_title: { type: :string }, + topic_html_title: { type: :string }, + category_id: { type: :integer }, + display_username: { type: :string }, + primary_group_name: { type: :string, nullable: true }, + primary_group_flair_url: { type: :string, nullable: true }, + primary_group_flair_bg_color: { type: :string, nullable: true }, + primary_group_flair_color: { type: :string, nullable: true }, + version: { type: :integer }, + can_edit: { type: :boolean }, + can_delete: { type: :boolean }, + can_recover: { type: :boolean }, + can_wiki: { type: :boolean }, + user_title: { type: :string, nullable: true }, + raw: { type: :string }, + 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: :string, nullable: true }, + user_deleted: { type: :boolean }, + edit_reason: { type: :string, nullable: true }, + can_view_edit_history: { type: :boolean }, + wiki: { type: :boolean }, + reviewable_id: { type: :string, nullable: true }, + reviewable_score_count: { type: :integer }, + reviewable_score_pending_count: { type: :integer }, + } + }, + }, + } + + let!(:post) { Fabricate(:post) } + run_test! + end + end + + post 'Creates a new topic, a new post, or a private message' do + tags 'Posts' + consumes 'application/json' + parameter name: 'Api-Key', in: :header, type: :string, required: true + parameter name: 'Api-Username', in: :header, type: :string, required: true + # Can't be named :post! + parameter name: :post_body, in: :body, schema: { + type: :object, + properties: { + title: { + type: :string, + description: 'Required if creating a new topic or new private message.' + }, + topic_id: { + type: :integer, + description: 'Required if creating a new post.' + }, + raw: { type: :string }, + category: { + type: :integer, + description: 'Optional if creating a new topic, and ignored if creating a new post.' + }, + target_usernames: { + type: :string, + description: 'Required for private message, comma separated.', + example: 'blake,sam' + }, + archetype: { type: :string }, + created_at: { type: :string }, + }, + required: [ 'raw' ] + } + + produces 'application/json' + response '200', 'post created' do + schema type: :object, properties: { + id: { type: :integer }, + name: { type: :string, nullable: true }, + 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: :string, nullable: true }, + quote_count: { type: :integer }, + incoming_link_count: { type: :integer }, + reads: { type: :integer }, + readers_count: { type: :integer }, + score: { type: :integer }, + yours: { type: :boolean }, + topic_id: { type: :integer }, + topic_slug: { type: :string }, + display_username: { type: :string, nullable: true }, + primary_group_name: { type: :string, nullable: true }, + primary_group_flair_url: { type: :string, nullable: true }, + primary_group_flair_bg_color: { type: :string, nullable: true }, + primary_group_flair_color: { type: :string, nullable: true }, + version: { type: :integer }, + can_edit: { type: :boolean }, + can_delete: { type: :boolean }, + can_recover: { type: :boolean }, + can_wiki: { type: :boolean }, + user_title: { type: :string, nullable: true }, + 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 }, + draft_sequence: { type: :integer }, + hidden: { type: :boolean }, + trust_level: { type: :integer }, + deleted_at: { type: :string, nullable: true }, + user_deleted: { type: :boolean }, + edit_reason: { type: :string, nullable: true }, + can_view_edit_history: { type: :boolean }, + wiki: { type: :boolean }, + reviewable_id: { type: :string, nullable: true }, + reviewable_score_count: { type: :integer }, + reviewable_score_pending_count: { type: :integer }, + } + + let(:post_body) { Fabricate(:post) } + run_test! + end + end + end + + path '/posts/{id}.json' do + + get 'Retreive a single post' do + tags 'Posts' + 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', 'latest posts' do + schema type: :object, properties: { + id: { type: :integer }, + name: { type: :string, nullable: true }, + 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: :string, nullable: true }, + quote_count: { type: :integer }, + incoming_link_count: { type: :integer }, + reads: { type: :integer }, + readers_count: { type: :integer }, + score: { type: :integer }, + yours: { type: :boolean }, + topic_id: { type: :integer }, + topic_slug: { type: :string }, + display_username: { type: :string, nullable: true }, + primary_group_name: { type: :string, nullable: true }, + primary_group_flair_url: { type: :string, nullable: true }, + primary_group_flair_bg_color: { type: :string, nullable: true }, + primary_group_flair_color: { type: :string, nullable: true }, + version: { type: :integer }, + can_edit: { type: :boolean }, + can_delete: { type: :boolean }, + can_recover: { type: :boolean }, + can_wiki: { type: :boolean }, + user_title: { type: :string, nullable: true }, + raw: { type: :string }, + 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: :string, nullable: true }, + user_deleted: { type: :boolean }, + edit_reason: { type: :string, nullable: true }, + can_view_edit_history: { type: :boolean }, + wiki: { type: :boolean }, + reviewable_id: { type: :string, nullable: true }, + reviewable_score_count: { type: :integer }, + reviewable_score_pending_count: { type: :integer }, + } + + let(:id) { Fabricate(:post).id } + run_test! + end + end + + put 'Update a single post' do + tags 'Posts' + 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: { + type: :object, + properties: { + raw: { type: :string }, + edit_reason: { type: :string }, + }, required: [ 'raw' ] + } + } + } + + produces 'application/json' + response '200', 'post updated' do + schema type: :object, properties: { + post: { + type: :object, + properties: { + id: { type: :integer }, + name: { type: :string, nullable: true }, + 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: :string, nullable: true }, + 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: :string, nullable: true }, + primary_group_name: { type: :string, nullable: true }, + primary_group_flair_url: { type: :string, nullable: true }, + primary_group_flair_bg_color: { type: :string, nullable: true }, + primary_group_flair_color: { type: :string, nullable: true }, + version: { type: :integer }, + can_edit: { type: :boolean }, + can_delete: { type: :boolean }, + can_recover: { type: :boolean }, + can_wiki: { type: :boolean }, + user_title: { type: :string, nullable: true }, + 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 }, + draft_sequence: { type: :integer }, + hidden: { type: :boolean }, + trust_level: { type: :integer }, + deleted_at: { type: :string, nullable: true }, + user_deleted: { type: :boolean }, + edit_reason: { type: :string, nullable: true }, + can_view_edit_history: { type: :boolean }, + wiki: { type: :boolean }, + reviewable_id: { type: :string, nullable: true }, + reviewable_score_count: { type: :integer }, + reviewable_score_pending_count: { type: :integer }, + } + }, + } + + let(:post_body) { { 'post': { 'raw': 'Updated content!', 'edit_reason': 'fixed typo' } } } + let(:id) { Fabricate(:post).id } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['post']['cooked']).to eq("

Updated content!

") + expect(data['post']['edit_reason']).to eq("fixed typo") + end + end + end + end + + path '/posts/{id}/locked.json' do + put 'Lock a post from being edited' do + tags 'Posts' + 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: { + locked: { type: :boolean } + }, required: [ 'locked' ] + } + + produces 'application/json' + response '200', 'post updated' do + schema type: :object, properties: { + locked: { type: :boolean }, + } + + let(:post_body) { { 'locked': 'true' } } + let(:id) { Fabricate(:post).id } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['locked']).to eq(true) + end + end + end + end + + path '/post_actions.json' do + post 'Like a post and other actions' do + tags 'Posts' + 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: :post_body, in: :body, schema: { + type: :object, + properties: { + id: { type: :integer }, + post_action_type_id: { type: :integer }, + flag_topic: { type: :boolean }, + }, required: [ 'id', 'post_action_type_id' ] + } + + produces 'application/json' + response '200', 'post updated' do + schema type: :object, properties: { + id: { type: :integer }, + name: { type: :string }, + 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: :string, nullable: true }, + 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: :string }, + primary_group_name: { type: :string, nullable: true }, + primary_group_flair_url: { type: :string, nullable: true }, + primary_group_flair_bg_color: { type: :string, nullable: true }, + primary_group_flair_color: { type: :string, nullable: true }, + version: { type: :integer }, + can_edit: { type: :boolean }, + can_delete: { type: :boolean }, + can_recover: { type: :boolean }, + can_wiki: { type: :boolean }, + user_title: { type: :string, nullable: true }, + actions_summary: { + type: :array, + items: { + type: :object, + properties: { + id: { type: :integer }, + count: { type: :integer }, + acted: { type: :boolean }, + can_undo: { 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: :string, nullable: true }, + user_deleted: { type: :boolean }, + edit_reason: { type: :string, nullable: true }, + can_view_edit_history: { type: :boolean }, + wiki: { type: :boolean }, + notice_type: { type: :string }, + reviewable_id: { type: :string, nullable: true }, + reviewable_score_count: { type: :integer }, + reviewable_score_pending_count: { type: :integer }, + } + + let(:id) { Fabricate(:post).id } + let(:post_body) { { id: id, post_action_type_id: 2 } } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['actions_summary'][0]['id']).to eq(2) + expect(data['actions_summary'][0]['count']).to eq(1) + end + end + end + end + +end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb new file mode 100644 index 00000000000..925f3be0837 --- /dev/null +++ b/spec/requests/api/users_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true +require 'swagger_helper' + +describe 'users' do + + let(:'Api-Key') { Fabricate(:api_key).key } + let(:'Api-Username') { 'system' } + + path '/users.json' do + + post 'Creates a user' do + tags 'Users' + 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: :user_body, in: :body, schema: { + type: :object, + properties: { + "name": { type: :string }, + "email": { type: :string }, + "password": { type: :string }, + "username": { type: :string }, + "active": { type: :boolean }, + "approved": { type: :boolean }, + "user_fields[1]": { type: :string }, + }, + required: ['name', 'email', 'password', 'username'] + } + + produces 'application/json' + response '200', 'user created' do + schema type: :object, properties: { + success: { type: :boolean }, + active: { type: :boolean }, + message: { type: :string }, + user_id: { type: :integer }, + } + + let(:user_body) { { + name: 'user', + username: 'user1', + email: 'user1@example.com', + password: '13498428e9597cab689b468ebc0a5d33', + active: true + } } + run_test! do |response| + data = JSON.parse(response.body) + expect(data['success']).to eq(true) + expect(data['active']).to eq(true) + end + end + end + + end +end