2020-04-27 18:40:07 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'rails_helper'
|
2021-01-21 18:28:08 -05:00
|
|
|
require 'json_schemer'
|
|
|
|
|
|
|
|
# Require schema files
|
|
|
|
Dir["./spec/requests/api/schemas/*.rb"].each { |file| require file }
|
|
|
|
|
|
|
|
# Require shared spec examples
|
|
|
|
Dir["./spec/requests/api/shared/*.rb"].each { |file| require file }
|
2020-04-27 18:40:07 -04:00
|
|
|
|
2021-01-26 09:38:46 -05:00
|
|
|
def load_spec_schema(name)
|
|
|
|
SpecSchemas::SpecLoader.new(name).load
|
|
|
|
end
|
|
|
|
|
2021-02-04 20:43:40 -05:00
|
|
|
def api_docs_description
|
|
|
|
<<~HEREDOC
|
|
|
|
This page contains the documentation on how to use Discourse through API calls.
|
|
|
|
|
|
|
|
> Note: For any endpoints not listed you can follow the
|
|
|
|
[reverse engineer the Discourse API](https://meta.discourse.org/t/-/20576)
|
|
|
|
guide to figure out how to use an API endpoint.
|
|
|
|
|
|
|
|
### Request Content-Type
|
|
|
|
|
|
|
|
The Content-Type for POST and PUT requests can be set to `application/x-www-form-urlencoded`,
|
|
|
|
`multipart/form-data`, or `application/json`.
|
|
|
|
|
|
|
|
### Endpoint Names and Response Content-Type
|
|
|
|
|
|
|
|
Most API endpoints provide the same content as their HTML counterparts. For example
|
|
|
|
the URL `/categories` serves a list of categories, the `/categories.json` API provides the
|
|
|
|
same information in JSON format.
|
|
|
|
|
|
|
|
Instead of sending API requests to `/categories.json` you may also send them to `/categories`
|
|
|
|
and add an `Accept: application/json` header to the request to get the JSON response.
|
|
|
|
Sending requests with the `Accept` header is necessary if you want to use URLs
|
|
|
|
for related endpoints returned by the API, such as pagination URLs.
|
|
|
|
These URLs are returned without the `.json` prefix so you need to add the header in
|
|
|
|
order to get the correct response format.
|
|
|
|
|
|
|
|
### Authentication
|
|
|
|
|
|
|
|
Some endpoints do not require any authentication, pretty much anything else will
|
|
|
|
require you to be authenticated.
|
|
|
|
|
|
|
|
To become authenticated you will need to create an API Key from the admin panel.
|
|
|
|
|
|
|
|
Once you have your API Key you can pass it in along with your API Username
|
|
|
|
as an HTTP header like this:
|
|
|
|
|
|
|
|
```
|
2021-02-08 12:09:44 -05:00
|
|
|
curl -X GET "http://127.0.0.1:3000/admin/users/list/active.json" \\
|
|
|
|
-H "Api-Key: 714552c6148e1617aeab526d0606184b94a80ec048fc09894ff1a72b740c5f19" \\
|
2021-02-04 20:43:40 -05:00
|
|
|
-H "Api-Username: system"
|
|
|
|
```
|
|
|
|
|
|
|
|
and this is how POST requests will look:
|
|
|
|
|
|
|
|
```
|
2021-02-08 12:09:44 -05:00
|
|
|
curl -X POST "http://127.0.0.1:3000/categories" \\
|
|
|
|
-H "Content-Type: multipart/form-data;" \\
|
|
|
|
-H "Api-Key: 714552c6148e1617aeab526d0606184b94a80ec048fc09894ff1a72b740c5f19" \\
|
|
|
|
-H "Api-Username: system" \\
|
|
|
|
-F "name=89853c20-4409-e91a-a8ea-f6cdff96aaaa" \\
|
|
|
|
-F "color=49d9e9" \\
|
2021-02-04 20:43:40 -05:00
|
|
|
-F "text_color=f0fcfd"
|
|
|
|
```
|
|
|
|
|
|
|
|
### Boolean values
|
|
|
|
|
|
|
|
If an endpoint accepts a boolean be sure to specify it as a lowercase
|
|
|
|
`true` or `false` value unless noted otherwise.
|
|
|
|
HEREDOC
|
|
|
|
end
|
|
|
|
|
2020-04-27 18:40:07 -04:00
|
|
|
RSpec.configure do |config|
|
|
|
|
# Specify a root folder where Swagger JSON files are generated
|
|
|
|
# NOTE: If you're using the rswag-api to serve API descriptions, you'll need
|
|
|
|
# to ensure that it's configured to serve Swagger from the same folder
|
|
|
|
config.swagger_root = Rails.root.join('openapi').to_s
|
|
|
|
|
|
|
|
# Define one or more Swagger documents and provide global metadata for each one
|
|
|
|
# When you run the 'rswag:specs:swaggerize' rake task, the complete Swagger will
|
|
|
|
# be generated at the provided relative path under swagger_root
|
|
|
|
# By default, the operations defined in spec files are added to the first
|
|
|
|
# document below. You can override this behavior by adding a swagger_doc tag to the
|
|
|
|
# the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
|
|
|
|
config.swagger_docs = {
|
|
|
|
'openapi.yaml' => {
|
|
|
|
openapi: '3.0.3',
|
|
|
|
info: {
|
|
|
|
title: 'Discourse API Documentation',
|
2021-02-04 20:43:40 -05:00
|
|
|
'x-logo': {
|
|
|
|
url: 'https://discourse-meta.s3-us-west-1.amazonaws.com/optimized/3X/9/d/9d543e92b15b06924249654667a81441a55867eb_1_690x184.png',
|
|
|
|
},
|
|
|
|
version: 'latest',
|
|
|
|
description: api_docs_description
|
2020-04-27 18:40:07 -04:00
|
|
|
},
|
|
|
|
paths: {},
|
|
|
|
servers: [
|
|
|
|
{
|
|
|
|
url: 'https://{defaultHost}',
|
|
|
|
variables: {
|
|
|
|
defaultHost: {
|
|
|
|
default: 'discourse.example.com'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-14 18:59:58 -05:00
|
|
|
],
|
|
|
|
components: {
|
|
|
|
schemas: {
|
|
|
|
user_response: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
user_badges: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
user: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
id: { type: :integer },
|
|
|
|
username: { type: :string },
|
|
|
|
name: { type: :string },
|
|
|
|
avatar_template: { type: :string },
|
|
|
|
last_posted_at: { type: :string, nullable: true },
|
|
|
|
last_seen_at: { type: :string, nullable: true },
|
|
|
|
created_at: { type: :string },
|
|
|
|
ignored: { type: :boolean },
|
|
|
|
muted: { type: :boolean },
|
|
|
|
can_ignore_user: { type: :boolean },
|
|
|
|
can_mute_user: { type: :boolean },
|
|
|
|
can_send_private_messages: { type: :boolean },
|
|
|
|
can_send_private_message_to_user: { type: :boolean },
|
|
|
|
trust_level: { type: :integer },
|
|
|
|
moderator: { type: :boolean },
|
|
|
|
admin: { type: :boolean },
|
|
|
|
title: { type: :string, nullable: true },
|
|
|
|
badge_count: { type: :integer },
|
|
|
|
user_fields: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
}
|
|
|
|
},
|
|
|
|
custom_fields: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
}
|
|
|
|
},
|
|
|
|
time_read: { type: :integer },
|
|
|
|
recent_time_read: { type: :integer },
|
|
|
|
primary_group_id: { 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 },
|
|
|
|
featured_topic: { type: :string, nullable: true },
|
|
|
|
staged: { type: :boolean },
|
|
|
|
can_edit: { type: :boolean },
|
|
|
|
can_edit_username: { type: :boolean },
|
|
|
|
can_edit_email: { type: :boolean },
|
|
|
|
can_edit_name: { type: :boolean },
|
|
|
|
uploaded_avatar_id: { type: :string, nullable: true },
|
|
|
|
has_title_badges: { type: :boolean },
|
|
|
|
pending_count: { type: :integer },
|
|
|
|
profile_view_count: { type: :integer },
|
|
|
|
second_factor_enabled: { type: :boolean },
|
|
|
|
can_upload_profile_header: { type: :boolean },
|
|
|
|
can_upload_user_card_background: { type: :boolean },
|
|
|
|
post_count: { type: :integer },
|
|
|
|
can_be_deleted: { type: :boolean },
|
|
|
|
can_delete_all_posts: { type: :boolean },
|
|
|
|
locale: { type: :string, nullable: true },
|
|
|
|
muted_category_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
regular_category_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
watched_tags: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
watching_first_post_tags: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tracked_tags: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
muted_tags: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tracked_category_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
watched_category_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
watched_first_post_category_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
system_avatar_upload_id: { type: :string, nullable: true },
|
|
|
|
system_avatar_template: { type: :string },
|
|
|
|
muted_usernames: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ignored_usernames: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
allowed_pm_usernames: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mailing_list_posts_per_day: { type: :integer },
|
|
|
|
can_change_bio: { type: :boolean },
|
|
|
|
can_change_location: { type: :boolean },
|
|
|
|
can_change_website: { type: :boolean },
|
|
|
|
user_api_keys: { type: :string, nullable: true },
|
|
|
|
user_auth_tokens: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
id: { type: :integer },
|
|
|
|
client_ip: { type: :string },
|
|
|
|
location: { type: :string },
|
|
|
|
browser: { type: :string },
|
|
|
|
device: { type: :string },
|
|
|
|
os: { type: :string },
|
|
|
|
icon: { type: :string },
|
|
|
|
created_at: { type: :string },
|
|
|
|
seen_at: { type: :string },
|
|
|
|
is_active: { type: :boolean },
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
featured_user_badge_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
invited_by: { type: :string, nullable: true },
|
|
|
|
groups: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
id: { type: :integer },
|
|
|
|
automatic: { type: :boolean },
|
|
|
|
name: { type: :string },
|
|
|
|
display_name: { type: :string },
|
|
|
|
user_count: { type: :integer },
|
|
|
|
mentionable_level: { type: :integer },
|
|
|
|
messageable_level: { type: :integer },
|
|
|
|
visibility_level: { type: :integer },
|
|
|
|
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 },
|
|
|
|
members_visibility_level: { type: :integer },
|
|
|
|
can_see_members: { type: :boolean },
|
|
|
|
can_admin_group: { type: :boolean },
|
|
|
|
publish_read_state: { type: :boolean },
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
group_users: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
group_id: { type: :integer },
|
|
|
|
user_id: { type: :integer },
|
|
|
|
notification_level: { type: :integer },
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
user_option: {
|
|
|
|
type: :object,
|
|
|
|
properties: {
|
|
|
|
user_id: { type: :integer },
|
|
|
|
mailing_list_mode: { type: :boolean },
|
|
|
|
mailing_list_mode_frequency: { type: :integer },
|
|
|
|
email_digests: { type: :boolean },
|
|
|
|
email_level: { type: :integer },
|
|
|
|
email_messages_level: { type: :integer },
|
|
|
|
external_links_in_new_tab: { type: :boolean },
|
|
|
|
color_scheme_id: { type: :string, nullable: true },
|
|
|
|
dark_scheme_id: { type: :string, nullable: true },
|
|
|
|
dynamic_favicon: { type: :boolean },
|
|
|
|
enable_quoting: { type: :boolean },
|
|
|
|
enable_defer: { type: :boolean },
|
|
|
|
digest_after_minutes: { type: :integer },
|
|
|
|
automatically_unpin_topics: { type: :boolean },
|
|
|
|
auto_track_topics_after_msecs: { type: :integer },
|
|
|
|
notification_level_when_replying: { type: :integer },
|
|
|
|
new_topic_duration_minutes: { type: :integer },
|
|
|
|
email_previous_replies: { type: :integer },
|
|
|
|
email_in_reply_to: { type: :boolean },
|
|
|
|
like_notification_frequency: { type: :integer },
|
|
|
|
include_tl0_in_digests: { type: :boolean },
|
|
|
|
theme_ids: {
|
|
|
|
type: :array,
|
|
|
|
items: {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
theme_key_seq: { type: :integer },
|
|
|
|
allow_private_messages: { type: :boolean },
|
|
|
|
enable_allowed_pm_users: { type: :boolean },
|
|
|
|
homepage_id: { type: :string, nullable: true },
|
|
|
|
hide_profile_and_presence: { type: :boolean },
|
|
|
|
text_size: { type: :string },
|
|
|
|
text_size_seq: { type: :integer },
|
|
|
|
title_count_mode: { type: :string },
|
|
|
|
timezone: { type: :string, nullable: true },
|
|
|
|
skip_new_user_tips: { type: :boolean },
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-27 18:40:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
|
|
|
|
# The swagger_docs configuration option has the filename including format in
|
|
|
|
# the key, this may want to be changed to avoid putting yaml in json files.
|
|
|
|
# Defaults to json. Accepts ':json' and ':yaml'.
|
|
|
|
config.swagger_format = :yaml
|
|
|
|
end
|