diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md new file mode 100644 index 0000000000000000000000000000000000000000..62094ffc940dd4235db98cb5e2cb82a977193656 --- /dev/null +++ b/doc/api/group_wikis.md @@ -0,0 +1,196 @@ +# Wikis API + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212199) in GitLab 13.2. + +Available only in APIv4. + +## List wiki pages + +List all wiki pages for a given group. + +```plaintext +GET /groups/:id/wikis +``` + +| Attribute | Type | Required | Description | +| --------- | ------- | -------- | --------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `with_content` | boolean | no | Include pages' content | + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/wikis?with_content=1" +``` + +Example response: + +```json +[ + { + "content" : "Here is an instruction how to deploy this project.", + "format" : "markdown", + "slug" : "deploy", + "title" : "deploy" + }, + { + "content" : "Our development process is described here.", + "format" : "markdown", + "slug" : "development", + "title" : "development" + },{ + "content" : "* [Deploy](deploy)\n* [Development](development)", + "format" : "markdown", + "slug" : "home", + "title" : "home" + } +] +``` + +## Get a wiki page + +Get a wiki page for a given group. + +```plaintext +GET /groups/:id/wikis/:slug +``` + +| Attribute | Type | Required | Description | +| --------- | ------- | -------- | --------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `slug` | string | yes | The slug (a unique string) of the wiki page | + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/wikis/home" +``` + +Example response: + +```json +{ + "content" : "home page", + "format" : "markdown", + "slug" : "home", + "title" : "home" +} +``` + +## Create a new wiki page + +Create a new wiki page for the given repository with the given title, slug, and content. + +```plaintext +POST /projects/:id/wikis +``` + +| Attribute | Type | Required | Description | +| ------------- | ------- | -------- | ---------------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `content` | string | yes | The content of the wiki page | +| `title` | string | yes | The title of the wiki page | +| `format` | string | no | The format of the wiki page. Available formats are: `markdown` (default), `rdoc`, `asciidoc` and `org` | + +```shell +curl --data "format=rdoc&title=Hello&content=Hello world" \ + --header "PRIVATE-TOKEN: <your_access_token>" \ + "https://gitlab.example.com/api/v4/groups/1/wikis" +``` + +Example response: + +```json +{ + "content" : "Hello world", + "format" : "markdown", + "slug" : "Hello", + "title" : "Hello" +} +``` + +## Edit an existing wiki page + +Update an existing wiki page. At least one parameter is required to update the wiki page. + +```plaintext +PUT /groups/:id/wikis/:slug +``` + +| Attribute | Type | Required | Description | +| --------------- | ------- | --------------------------------- | ------------------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `content` | string | yes if `title` is not provided | The content of the wiki page | +| `title` | string | yes if `content` is not provided | The title of the wiki page | +| `format` | string | no | The format of the wiki page. Available formats are: `markdown` (default), `rdoc`, `asciidoc` and `org` | +| `slug` | string | yes | The slug (a unique identifier) of the wiki page | + +```shell +curl --request PUT --data "format=rdoc&content=documentation&title=Docs" \ + --header "PRIVATE-TOKEN: <your_access_token>" \ + "https://gitlab.example.com/api/v4/groups/1/wikis/foo" +``` + +Example response: + +```json +{ + "content" : "documentation", + "format" : "markdown", + "slug" : "Docs", + "title" : "Docs" +} +``` + +## Delete a wiki page + +Delete a wiki page with a given slug. + +```plaintext +DELETE /groups/:id/wikis/:slug +``` + +| Attribute | Type | Required | Description | +| --------- | ------- | -------- | --------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `slug` | string | yes | The slug (a unique identifier) of the wiki page | + +```shell +curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/wikis/foo" +``` + +On success the HTTP status code is `204` and no JSON response is expected. + +## Upload an attachment to the wiki repository + +Upload a file to the attachment folder inside the wiki's repository. The +attachment folder is the `uploads` folder. + +```plaintext +POST /groups/:id/wikis/attachments +``` + +| Attribute | Type | Required | Description | +| ------------- | ------- | -------- | ---------------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `file` | string | yes | The attachment to be uploaded | +| `branch` | string | no | The name of the branch. Defaults to the wiki repository default branch | + +To upload a file from your filesystem, use the `--form` argument. This causes +cURL to post data using the header `Content-Type: multipart/form-data`. +The `file=` parameter must point to a file on your filesystem and be preceded +by `@`. For example: + +```shell +curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "file=@dk.png" "https://gitlab.example.com/api/v4/groups/1/wikis/attachments" +``` + +Example response: + +```json +{ + "file_name" : "dk.png", + "file_path" : "uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png", + "branch" : "master", + "link" : { + "url" : "uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png", + "markdown" : "" + } +} +``` diff --git a/ee/changelogs/unreleased/ajk-group-wiki-attachments.yml b/ee/changelogs/unreleased/ajk-group-wiki-attachments.yml new file mode 100644 index 0000000000000000000000000000000000000000..e1652625e271520d80837aa0cb12e19752222a4d --- /dev/null +++ b/ee/changelogs/unreleased/ajk-group-wiki-attachments.yml @@ -0,0 +1,5 @@ +--- +title: Add group wiki REST API +merge_request: 34232 +author: +type: added diff --git a/ee/lib/ee/api/helpers/wikis_helpers.rb b/ee/lib/ee/api/helpers/wikis_helpers.rb new file mode 100644 index 0000000000000000000000000000000000000000..f563cf1b68b4e25cd2762bbb5c4ff8cbc0c7abef --- /dev/null +++ b/ee/lib/ee/api/helpers/wikis_helpers.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module EE + module API + module Helpers + module WikisHelpers + extend ActiveSupport::Concern + extend ::Gitlab::Utils::Override + + class_methods do + def wiki_resource_kinds + [:groups, *super] + end + end + + override :find_container + def find_container(kind) + return user_group if kind == :groups + + super + end + end + end + end +end diff --git a/ee/spec/requests/api/wikis_spec.rb b/ee/spec/requests/api/wikis_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..c144c0be9e2698362d97c70b36c3fe006a3c0161 --- /dev/null +++ b/ee/spec/requests/api/wikis_spec.rb @@ -0,0 +1,701 @@ +# frozen_string_literal: true + +require 'spec_helper' + +# For every API endpoint we test 3 states of wikis: +# - disabled +# - enabled only for team members +# - enabled for everyone who has access +# Every state is tested for 3 user roles: +# - guest +# - developer +# - maintainer +# because they are 3 edge cases of using wiki pages. + +describe API::Wikis do + include WorkhorseHelpers + + let_it_be(:user) { create(:user) } + let(:group) { create(:group, :internal, :wiki_repo) } + let(:wiki) { create(:group_wiki, container: group, user: user) } + let(:payload) { { content: 'content', format: 'rdoc', title: 'title' } } + let(:expected_keys_with_content) { %w(content format slug title) } + let(:expected_keys_without_content) { %w(format slug title) } + + shared_examples_for 'wiki API 404 Group Not Found' do + include_examples 'wiki API 404 Not Found', 'Group' + end + + describe 'GET /groups/:id/wikis' do + let(:url) { "/groups/#{group.id}/wikis" } + + context 'when group wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + get api(url, user) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + get api(url, user) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + # Skipped pending https://gitlab.com/gitlab-org/gitlab/-/issues/208412 + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_repo, :wiki_private) } + + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API returns list of wiki pages' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API returns list of wiki pages' + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API returns list of wiki pages' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API returns list of wiki pages' + end + end + end + + describe 'GET /groups/:id/wikis/:slug' do + let(:page) { create(:wiki_page, wiki: wiki) } + let(:url) { "/groups/#{group.id}/wikis/#{page.slug}" } + + context 'when wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + get api(url, user) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + get api(url, user) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + # Skipped pending https://gitlab.com/gitlab-org/gitlab/-/issues/208412 + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_repo, :wiki_private) } + + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + get api(url, user) + end + + include_examples 'wikis API returns wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + get api(url, user) + end + + include_examples 'wikis API returns wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + get api(url) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + get api(url, user) + end + + include_examples 'wikis API returns wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + get api(url, user) + end + + include_examples 'wikis API returns wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + end + end + + describe 'POST /groups/:id/wikis' do + let(:payload) { { title: 'title', content: 'content' } } + let(:url) { "/groups/#{group.id}/wikis" } + + context 'when wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + post(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + post(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_private, :wiki_repo) } + + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API creates wiki page' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API creates wiki page' + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API creates wiki page' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API creates wiki page' + end + end + end + + describe 'PUT /group/:id/wikis/:slug' do + let(:page) { create(:wiki_page, wiki: wiki) } + let(:payload) { { title: 'new title', content: 'new content' } } + let(:url) { "/groups/#{group.id}/wikis/#{page.slug}" } + + context 'when wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + put(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + put(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + put(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_private, :wiki_repo) } + + context 'when user is guest' do + before do + put(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API updates wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + before do + put(api(url, user), params: payload) + end + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API updates wiki page' + + context 'when page is not existing' do + let(:url) { "/group/#{group.id}/wikis/unknown" } + + before do + put(api(url, user), params: payload) + end + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + put(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wikis API updates wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + before do + put(api(url, user), params: payload) + end + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wikis API updates wiki page' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + before do + put(api(url, user), params: payload) + end + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + end + + context 'when user is owner of parent group' do + let(:namespace) { create(:group).tap { |g| g.add_owner(user) } } + let(:group) { create(:group, :wiki_repo, parent: namespace) } + + include_examples 'wikis API updates wiki page' + end + end + + describe 'DELETE /groups/:id/wikis/:slug' do + let(:page) { create(:wiki_page, wiki: wiki) } + let(:url) { "/groups/#{group.id}/wikis/#{page.slug}" } + + context 'when wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + delete(api(url)) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + # Skipped pending https://gitlab.com/gitlab-org/gitlab/-/issues/208412 + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_repo, :wiki_private) } + + context 'when user is guest' do + before do + delete(api(url)) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 204 No Content' + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + delete(api(url)) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + + delete(api(url, user)) + end + + include_examples 'wiki API 204 No Content' + + context 'when page does not exist' do + let(:url) { "/groups/#{group.id}/wikis/unknown" } + + include_examples 'wiki API 404 Wiki Page Not Found' + end + end + end + + context 'when user is owner of parent group' do + let(:namespace) { create(:group).tap { |g| g.add_owner(user) } } + let(:group) { create(:group, :wiki_repo, parent: namespace) } + + before do + delete(api(url, user)) + end + + include_examples 'wiki API 204 No Content' + end + end + + describe 'POST /groups/:id/wikis/attachments' do + let(:payload) { { file: fixture_file_upload('spec/fixtures/dk.png') } } + let(:url) { "/groups/#{group.id}/wikis/attachments" } + let(:file_path) { "#{Wikis::CreateAttachmentService::ATTACHMENT_PATH}/fixed_hex/dk.png" } + let(:result_hash) do + { + file_name: 'dk.png', + file_path: file_path, + branch: 'master', + link: { + url: file_path, + markdown: "" + } + } + end + + context 'when wiki is disabled' do + before do + stub_feature_flags(group_wiki: false) + end + + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + post(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + post(api(url, user), params: payload) + end + + include_examples 'wiki API 403 Forbidden' + end + end + + # Skipped pending https://gitlab.com/gitlab-org/gitlab/-/issues/208412 + xcontext 'when wiki is available only for team members' do + let(:group) { create(:group, :wiki_private, :wiki_repo) } + + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wiki API uploads wiki attachment' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wiki API uploads wiki attachment' + end + end + + context 'when wiki is available for everyone with access' do + context 'when user is guest' do + before do + post(api(url), params: payload) + end + + include_examples 'wiki API 404 Group Not Found' + end + + context 'when user is developer' do + before do + group.add_developer(user) + end + + include_examples 'wiki API uploads wiki attachment' + end + + context 'when user is maintainer' do + before do + group.add_maintainer(user) + end + + include_examples 'wiki API uploads wiki attachment' + end + end + end +end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index b1bbc3cf0fde371ad68397c8f685c205a0b71498..d8569dea4f1d1d5168bbc36bfef1c8c1bdab6ba4 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -79,12 +79,6 @@ def user_project @project ||= find_project!(params[:id]) end - def wiki_page - page = ProjectWiki.new(user_project, current_user).find_page(params[:slug]) - - page || not_found!('Wiki Page') - end - def available_labels_for(label_parent, include_ancestor_groups: true) search_params = { include_ancestor_groups: include_ancestor_groups } diff --git a/lib/api/helpers/wikis_helpers.rb b/lib/api/helpers/wikis_helpers.rb new file mode 100644 index 0000000000000000000000000000000000000000..49da1e317abf839e06c1f98cc6a3084fa2a06967 --- /dev/null +++ b/lib/api/helpers/wikis_helpers.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module API + module Helpers + module WikisHelpers + def self.wiki_resource_kinds + [:projects] + end + + def find_container(kind) + return user_project if kind == :projects + + raise "Unknown wiki container #{kind}" + end + + def wiki_page + Wiki.for_container(container, current_user).find_page(params[:slug]) || not_found!('Wiki Page') + end + + def commit_params(attrs) + base_params = { branch_name: attrs[:branch] } + file_details = case attrs[:file] + when Hash # legacy format: TODO remove when we drop support for non accelerated uploads + { file_name: attrs[:file][:filename], file_content: attrs[:file][:tempfile].read } + else + { file_name: attrs[:file].original_filename, file_content: attrs[:file].read } + end + + base_params.merge(file_details) + end + end + end +end + +API::Helpers::WikisHelpers.prepend_if_ee('EE::API::Helpers::WikisHelpers') diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb index c1bf3a6492326b16f3ea5b6ac41d1a383fa2ac35..006dc257fe1dae186e96c47ec6441667edb15a71 100644 --- a/lib/api/wikis.rb +++ b/lib/api/wikis.rb @@ -2,24 +2,10 @@ module API class Wikis < Grape::API + helpers ::API::Helpers::WikisHelpers + helpers do - def commit_params(attrs) - # In order to avoid service disruption this can work with an old workhorse without the acceleration - # the first branch of this if must be removed when we drop support for non accelerated uploads - if attrs[:file].is_a?(Hash) - { - file_name: attrs[:file][:filename], - file_content: attrs[:file][:tempfile].read, - branch_name: attrs[:branch] - } - else - { - file_name: attrs[:file].original_filename, - file_content: attrs[:file].read, - branch_name: attrs[:branch] - } - end - end + attr_reader :container params :common_wiki_page_params do optional :format, @@ -32,108 +18,118 @@ def commit_params(attrs) WIKI_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(slug: API::NO_SLASH_URL_PART_REGEX) - resource :projects, requirements: WIKI_ENDPOINT_REQUIREMENTS do - desc 'Get a list of wiki pages' do - success Entities::WikiPageBasic - end - params do - optional :with_content, type: Boolean, default: false, desc: "Include pages' content" - end - get ':id/wikis' do - authorize! :read_wiki, user_project - - entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic + ::API::Helpers::WikisHelpers.wiki_resource_kinds.each do |container_resource| + resource container_resource, requirements: WIKI_ENDPOINT_REQUIREMENTS do + after_validation do + @container = Gitlab::Lazy.new { find_container(container_resource) } + end - present user_project.wiki.list_pages(load_content: params[:with_content]), with: entity - end + desc 'Get a list of wiki pages' do + success Entities::WikiPageBasic + end + params do + optional :with_content, type: Boolean, default: false, desc: "Include pages' content" + end + get ':id/wikis' do + authorize! :read_wiki, container - desc 'Get a wiki page' do - success Entities::WikiPage - end - params do - requires :slug, type: String, desc: 'The slug of a wiki page' - end - get ':id/wikis/:slug' do - authorize! :read_wiki, user_project + entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic - present wiki_page, with: Entities::WikiPage - end + present container.wiki.list_pages(load_content: params[:with_content]), with: entity + end - desc 'Create a wiki page' do - success Entities::WikiPage - end - params do - requires :title, type: String, desc: 'Title of a wiki page' - requires :content, type: String, desc: 'Content of a wiki page' - use :common_wiki_page_params - end - post ':id/wikis' do - authorize! :create_wiki, user_project + desc 'Get a wiki page' do + success Entities::WikiPage + end + params do + requires :slug, type: String, desc: 'The slug of a wiki page' + end + get ':id/wikis/:slug' do + authorize! :read_wiki, container - page = WikiPages::CreateService.new(container: user_project, current_user: current_user, params: params).execute + present wiki_page, with: Entities::WikiPage + end - if page.valid? - present page, with: Entities::WikiPage - else - render_validation_error!(page) + desc 'Create a wiki page' do + success Entities::WikiPage end - end + params do + requires :title, type: String, desc: 'Title of a wiki page' + requires :content, type: String, desc: 'Content of a wiki page' + use :common_wiki_page_params + end + post ':id/wikis' do + authorize! :create_wiki, container - desc 'Update a wiki page' do - success Entities::WikiPage - end - params do - optional :title, type: String, desc: 'Title of a wiki page' - optional :content, type: String, desc: 'Content of a wiki page' - use :common_wiki_page_params - at_least_one_of :content, :title, :format - end - put ':id/wikis/:slug' do - authorize! :create_wiki, user_project + page = WikiPages::CreateService.new(container: container, current_user: current_user, params: params).execute - page = WikiPages::UpdateService.new(container: user_project, current_user: current_user, params: params).execute(wiki_page) + if page.valid? + present page, with: Entities::WikiPage + else + render_validation_error!(page) + end + end - if page.valid? - present page, with: Entities::WikiPage - else - render_validation_error!(page) + desc 'Update a wiki page' do + success Entities::WikiPage + end + params do + optional :title, type: String, desc: 'Title of a wiki page' + optional :content, type: String, desc: 'Content of a wiki page' + use :common_wiki_page_params + at_least_one_of :content, :title, :format + end + put ':id/wikis/:slug' do + authorize! :create_wiki, container + + page = WikiPages::UpdateService + .new(container: container, current_user: current_user, params: params) + .execute(wiki_page) + + if page.valid? + present page, with: Entities::WikiPage + else + render_validation_error!(page) + end end - end - desc 'Delete a wiki page' - params do - requires :slug, type: String, desc: 'The slug of a wiki page' - end - delete ':id/wikis/:slug' do - authorize! :admin_wiki, user_project + desc 'Delete a wiki page' + params do + requires :slug, type: String, desc: 'The slug of a wiki page' + end + delete ':id/wikis/:slug' do + authorize! :admin_wiki, container - WikiPages::DestroyService.new(container: user_project, current_user: current_user).execute(wiki_page) + WikiPages::DestroyService + .new(container: container, current_user: current_user) + .execute(wiki_page) - no_content! - end + no_content! + end - desc 'Upload an attachment to the wiki repository' do - detail 'This feature was introduced in GitLab 11.3.' - success Entities::WikiAttachment - end - params do - requires :file, types: [::API::Validations::Types::SafeFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded' - optional :branch, type: String, desc: 'The name of the branch' - end - post ":id/wikis/attachments" do - authorize! :create_wiki, user_project - - result = ::Wikis::CreateAttachmentService.new( - container: user_project, - current_user: current_user, - params: commit_params(declared_params(include_missing: false)) - ).execute - - if result[:status] == :success - status(201) - present OpenStruct.new(result[:result]), with: Entities::WikiAttachment - else - render_api_error!(result[:message], 400) + desc 'Upload an attachment to the wiki repository' do + detail 'This feature was introduced in GitLab 11.3.' + success Entities::WikiAttachment + end + params do + requires :file, types: [::API::Validations::Types::SafeFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded' + optional :branch, type: String, desc: 'The name of the branch' + end + post ":id/wikis/attachments" do + authorize! :create_wiki, container + + result = ::Wikis::CreateAttachmentService.new( + container: container, + current_user: current_user, + params: commit_params(declared_params(include_missing: false)) + ).execute + + if result[:status] == :success + status(201) + present OpenStruct.new(result[:result]), with: Entities::WikiAttachment + else + render_api_error!(result[:message], 400) + end end end end diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index ea22e4421278c9ed108e100f69cfa7bb80390832..f271f8aa853bd58d4980b59335900301d3a08c58 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -21,178 +21,10 @@ let(:payload) { { content: 'content', format: 'rdoc', title: 'title' } } let(:expected_keys_with_content) { %w(content format slug title) } let(:expected_keys_without_content) { %w(format slug title) } + let(:wiki) { project_wiki } - shared_examples_for 'returns list of wiki pages' do - context 'when wiki has pages' do - let!(:pages) do - [create(:wiki_page, wiki: project_wiki, title: 'page1', content: 'content of page1'), - create(:wiki_page, wiki: project_wiki, title: 'page2.with.dot', content: 'content of page2')] - end - - it 'returns the list of wiki pages without content' do - get api(url, user) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.size).to eq(2) - - json_response.each_with_index do |page, index| - expect(page.keys).to match_array(expected_keys_without_content) - expect(page['slug']).to eq(pages[index].slug) - expect(page['title']).to eq(pages[index].title) - end - end - - it 'returns the list of wiki pages with content' do - get api(url, user), params: { with_content: 1 } - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.size).to eq(2) - - json_response.each_with_index do |page, index| - expect(page.keys).to match_array(expected_keys_with_content) - expect(page['content']).to eq(pages[index].content) - expect(page['slug']).to eq(pages[index].slug) - expect(page['title']).to eq(pages[index].title) - end - end - end - - it 'return the empty list of wiki pages' do - get api(url, user) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.size).to eq(0) - end - end - - shared_examples_for 'returns wiki page' do - it 'returns the wiki page' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.size).to eq(4) - expect(json_response.keys).to match_array(expected_keys_with_content) - expect(json_response['content']).to eq(page.content) - expect(json_response['slug']).to eq(page.slug) - expect(json_response['title']).to eq(page.title) - end - end - - shared_examples_for 'creates wiki page' do - it 'creates the wiki page' do - post(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:created) - expect(json_response.size).to eq(4) - expect(json_response.keys).to match_array(expected_keys_with_content) - expect(json_response['content']).to eq(payload[:content]) - expect(json_response['slug']).to eq(payload[:title].tr(' ', '-')) - expect(json_response['title']).to eq(payload[:title]) - expect(json_response['rdoc']).to eq(payload[:rdoc]) - end - - [:title, :content].each do |part| - it "responds with validation error on empty #{part}" do - payload.delete(part) - - post(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response.size).to eq(1) - expect(json_response['error']).to eq("#{part} is missing") - end - end - end - - shared_examples_for 'updates wiki page' do - it 'updates the wiki page' do - put(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.size).to eq(4) - expect(json_response.keys).to match_array(expected_keys_with_content) - expect(json_response['content']).to eq(payload[:content]) - expect(json_response['slug']).to eq(payload[:title].tr(' ', '-')) - expect(json_response['title']).to eq(payload[:title]) - end - - [:title, :content, :format].each do |part| - it "updates with wiki with missing #{part}" do - payload.delete(part) - - put(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:ok) - end - end - end - - shared_examples_for '403 Forbidden' do - it 'returns 403 Forbidden' do - expect(response).to have_gitlab_http_status(:forbidden) - expect(json_response.size).to eq(1) - expect(json_response['message']).to eq('403 Forbidden') - end - end - - shared_examples_for '404 Wiki Page Not Found' do - it 'returns 404 Wiki Page Not Found' do - expect(response).to have_gitlab_http_status(:not_found) - expect(json_response.size).to eq(1) - expect(json_response['message']).to eq('404 Wiki Page Not Found') - end - end - - shared_examples_for '404 Project Not Found' do - it 'returns 404 Project Not Found' do - expect(response).to have_gitlab_http_status(:not_found) - expect(json_response.size).to eq(1) - expect(json_response['message']).to eq('404 Project Not Found') - end - end - - shared_examples_for '204 No Content' do - it 'returns 204 No Content' do - expect(response).to have_gitlab_http_status(:no_content) - end - end - - shared_examples_for 'uploads wiki attachment' do - it 'pushes attachment to the wiki repository' do - allow(SecureRandom).to receive(:hex).and_return('fixed_hex') - - workhorse_post_with_file(api(url, user), file_key: :file, params: payload) - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to eq result_hash.deep_stringify_keys - end - - it 'responds with validation error on empty file' do - payload.delete(:file) - - post(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response.size).to eq(1) - expect(json_response['error']).to eq('file is missing') - end - - it 'responds with validation error on invalid temp file' do - payload[:file] = { tempfile: '/etc/hosts' } - - post(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response.size).to eq(1) - expect(json_response['error']).to eq('file is invalid') - end - - it 'is backward compatible with regular multipart uploads' do - allow(SecureRandom).to receive(:hex).and_return('fixed_hex') - - post(api(url, user), params: payload) - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to eq result_hash.deep_stringify_keys - end + shared_examples_for 'wiki API 404 Project Not Found' do + include_examples 'wiki API 404 Not Found', 'Project' end describe 'GET /projects/:id/wikis' do @@ -206,7 +38,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -216,7 +48,7 @@ get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -226,7 +58,7 @@ get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -238,7 +70,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -246,7 +78,7 @@ project.add_developer(user) end - include_examples 'returns list of wiki pages' + include_examples 'wikis API returns list of wiki pages' end context 'when user is maintainer' do @@ -254,7 +86,7 @@ project.add_maintainer(user) end - include_examples 'returns list of wiki pages' + include_examples 'wikis API returns list of wiki pages' end end @@ -266,7 +98,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -274,7 +106,7 @@ project.add_developer(user) end - include_examples 'returns list of wiki pages' + include_examples 'wikis API returns list of wiki pages' end context 'when user is maintainer' do @@ -282,7 +114,7 @@ project.add_maintainer(user) end - include_examples 'returns list of wiki pages' + include_examples 'wikis API returns list of wiki pages' end end end @@ -299,7 +131,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -309,7 +141,7 @@ get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -319,7 +151,7 @@ get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -331,7 +163,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -340,12 +172,12 @@ get api(url, user) end - include_examples 'returns wiki page' + include_examples 'wikis API returns wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end @@ -356,12 +188,12 @@ get api(url, user) end - include_examples 'returns wiki page' + include_examples 'wikis API returns wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end end @@ -374,7 +206,7 @@ get api(url) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -384,12 +216,12 @@ get api(url, user) end - include_examples 'returns wiki page' + include_examples 'wikis API returns wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end @@ -400,12 +232,12 @@ get api(url, user) end - include_examples 'returns wiki page' + include_examples 'wikis API returns wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end end @@ -423,7 +255,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -432,7 +264,7 @@ post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -441,7 +273,7 @@ post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -453,7 +285,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -461,7 +293,7 @@ project.add_developer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end context 'when user is maintainer' do @@ -469,7 +301,7 @@ project.add_maintainer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end end @@ -481,7 +313,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -489,7 +321,7 @@ project.add_developer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end context 'when user is maintainer' do @@ -497,7 +329,7 @@ project.add_maintainer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end end end @@ -515,7 +347,7 @@ put(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -525,7 +357,7 @@ put(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -535,7 +367,7 @@ put(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -547,7 +379,7 @@ put(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -555,7 +387,7 @@ project.add_developer(user) end - include_examples 'updates wiki page' + include_examples 'wikis API updates wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } @@ -564,7 +396,7 @@ put(api(url, user), params: payload) end - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end @@ -573,7 +405,7 @@ project.add_maintainer(user) end - include_examples 'updates wiki page' + include_examples 'wikis API updates wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } @@ -582,7 +414,7 @@ put(api(url, user), params: payload) end - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end end @@ -595,7 +427,7 @@ put(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -603,7 +435,7 @@ project.add_developer(user) end - include_examples 'updates wiki page' + include_examples 'wikis API updates wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } @@ -612,7 +444,7 @@ put(api(url, user), params: payload) end - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end @@ -621,7 +453,7 @@ project.add_maintainer(user) end - include_examples 'updates wiki page' + include_examples 'wikis API updates wiki page' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } @@ -630,7 +462,7 @@ put(api(url, user), params: payload) end - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end end @@ -638,7 +470,7 @@ context 'when wiki belongs to a group project' do let(:project) { create(:project, :wiki_repo, namespace: group) } - include_examples 'updates wiki page' + include_examples 'wikis API updates wiki page' end end @@ -654,7 +486,7 @@ delete(api(url)) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -664,7 +496,7 @@ delete(api(url, user)) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -674,7 +506,7 @@ delete(api(url, user)) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -686,7 +518,7 @@ delete(api(url)) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -696,7 +528,7 @@ delete(api(url, user)) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -706,7 +538,7 @@ delete(api(url, user)) end - include_examples '204 No Content' + include_examples 'wiki API 204 No Content' end end @@ -718,7 +550,7 @@ delete(api(url)) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -728,7 +560,7 @@ delete(api(url, user)) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -738,12 +570,12 @@ delete(api(url, user)) end - include_examples '204 No Content' + include_examples 'wiki API 204 No Content' context 'when page is not existing' do let(:url) { "/projects/#{project.id}/wikis/unknown" } - include_examples '404 Wiki Page Not Found' + include_examples 'wiki API 404 Wiki Page Not Found' end end end @@ -755,7 +587,7 @@ delete(api(url, user)) end - include_examples '204 No Content' + include_examples 'wiki API 204 No Content' end end @@ -783,7 +615,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -792,7 +624,7 @@ post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end context 'when user is maintainer' do @@ -801,7 +633,7 @@ post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -813,7 +645,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -821,7 +653,7 @@ project.add_developer(user) end - include_examples 'uploads wiki attachment' + include_examples 'wiki API uploads wiki attachment' end context 'when user is maintainer' do @@ -829,7 +661,7 @@ project.add_maintainer(user) end - include_examples 'uploads wiki attachment' + include_examples 'wiki API uploads wiki attachment' end end @@ -841,7 +673,7 @@ post(api(url), params: payload) end - include_examples '404 Project Not Found' + include_examples 'wiki API 404 Project Not Found' end context 'when user is developer' do @@ -849,7 +681,7 @@ project.add_developer(user) end - include_examples 'uploads wiki attachment' + include_examples 'wiki API uploads wiki attachment' end context 'when user is maintainer' do @@ -857,7 +689,7 @@ project.add_maintainer(user) end - include_examples 'uploads wiki attachment' + include_examples 'wiki API uploads wiki attachment' end end end diff --git a/spec/support/shared_examples/lib/wikis_api_examples.rb b/spec/support/shared_examples/lib/wikis_api_examples.rb new file mode 100644 index 0000000000000000000000000000000000000000..2e4c667d37e3a5a36f1aab87e389408aed40c306 --- /dev/null +++ b/spec/support/shared_examples/lib/wikis_api_examples.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +RSpec.shared_examples_for 'wikis API returns list of wiki pages' do + context 'when wiki has pages' do + let!(:pages) do + [create(:wiki_page, wiki: wiki, title: 'page1', content: 'content of page1'), + create(:wiki_page, wiki: wiki, title: 'page2.with.dot', content: 'content of page2')] + end + + it 'returns the list of wiki pages without content' do + get api(url, user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.size).to eq(2) + + json_response.each_with_index do |page, index| + expect(page.keys).to match_array(expected_keys_without_content) + expect(page['slug']).to eq(pages[index].slug) + expect(page['title']).to eq(pages[index].title) + end + end + + it 'returns the list of wiki pages with content' do + get api(url, user), params: { with_content: 1 } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.size).to eq(2) + + json_response.each_with_index do |page, index| + expect(page.keys).to match_array(expected_keys_with_content) + expect(page['content']).to eq(pages[index].content) + expect(page['slug']).to eq(pages[index].slug) + expect(page['title']).to eq(pages[index].title) + end + end + end + + it 'return the empty list of wiki pages' do + get api(url, user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.size).to eq(0) + end +end + +RSpec.shared_examples_for 'wikis API returns wiki page' do + it 'returns the wiki page' do + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.size).to eq(4) + expect(json_response.keys).to match_array(expected_keys_with_content) + expect(json_response['content']).to eq(page.content) + expect(json_response['slug']).to eq(page.slug) + expect(json_response['title']).to eq(page.title) + end +end + +RSpec.shared_examples_for 'wikis API creates wiki page' do + it 'creates the wiki page' do + post(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:created) + expect(json_response.size).to eq(4) + expect(json_response.keys).to match_array(expected_keys_with_content) + expect(json_response['content']).to eq(payload[:content]) + expect(json_response['slug']).to eq(payload[:title].tr(' ', '-')) + expect(json_response['title']).to eq(payload[:title]) + expect(json_response['rdoc']).to eq(payload[:rdoc]) + end + + [:title, :content].each do |part| + it "responds with validation error on empty #{part}" do + payload.delete(part) + + post(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response.size).to eq(1) + expect(json_response['error']).to eq("#{part} is missing") + end + end +end + +RSpec.shared_examples_for 'wikis API updates wiki page' do + it 'updates the wiki page' do + put(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.size).to eq(4) + expect(json_response.keys).to match_array(expected_keys_with_content) + expect(json_response['content']).to eq(payload[:content]) + expect(json_response['slug']).to eq(payload[:title].tr(' ', '-')) + expect(json_response['title']).to eq(payload[:title]) + end + + [:title, :content, :format].each do |part| + it "updates with wiki with missing #{part}" do + payload.delete(part) + + put(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:ok) + end + end +end + +RSpec.shared_examples_for 'wiki API 403 Forbidden' do + it 'returns 403 Forbidden' do + expect(response).to have_gitlab_http_status(:forbidden) + expect(json_response.size).to eq(1) + expect(json_response['message']).to eq('403 Forbidden') + end +end + +RSpec.shared_examples_for 'wiki API 404 Wiki Page Not Found' do + it 'returns 404 Wiki Page Not Found' do + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response.size).to eq(1) + expect(json_response['message']).to eq('404 Wiki Page Not Found') + end +end + +RSpec.shared_examples_for 'wiki API 404 Not Found' do |what| + it "returns 404 #{what} Not Found" do + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response.size).to eq(1) + expect(json_response['message']).to eq("404 #{what} Not Found") + end +end + +RSpec.shared_examples_for 'wiki API 204 No Content' do + it 'returns 204 No Content' do + expect(response).to have_gitlab_http_status(:no_content) + end +end + +RSpec.shared_examples_for 'wiki API uploads wiki attachment' do + it 'pushes attachment to the wiki repository' do + allow(SecureRandom).to receive(:hex).and_return('fixed_hex') + + workhorse_post_with_file(api(url, user), file_key: :file, params: payload) + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to eq result_hash.deep_stringify_keys + end + + it 'responds with validation error on empty file' do + payload.delete(:file) + + post(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response.size).to eq(1) + expect(json_response['error']).to eq('file is missing') + end + + it 'responds with validation error on invalid temp file' do + payload[:file] = { tempfile: '/etc/hosts' } + + post(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response.size).to eq(1) + expect(json_response['error']).to eq('file is invalid') + end + + it 'is backward compatible with regular multipart uploads' do + allow(SecureRandom).to receive(:hex).and_return('fixed_hex') + + post(api(url, user), params: payload) + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to eq result_hash.deep_stringify_keys + end +end