diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-25 06:08:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-25 06:08:52 +0300 |
commit | 03c38e31112a7dc0de78325e5421e6bb08c508ed (patch) | |
tree | 945f4eb57701ea8887d9c9beca06c67d9d01ad8d | |
parent | 5d6119a1a4d31009ea7aee400c8462e897ddc26c (diff) |
Add latest changes from gitlab-org/gitlab@master
25 files changed, 718 insertions, 409 deletions
diff --git a/app/graphql/resolvers/release_resolver.rb b/app/graphql/resolvers/release_resolver.rb index 9bae8b8cd13..1edcc8c70b5 100644 --- a/app/graphql/resolvers/release_resolver.rb +++ b/app/graphql/resolvers/release_resolver.rb @@ -15,6 +15,8 @@ module Resolvers end def resolve(tag_name:) + return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true) + ReleasesFinder.new( project, current_user, diff --git a/app/graphql/resolvers/releases_resolver.rb b/app/graphql/resolvers/releases_resolver.rb index b2afbb92684..85892c2abeb 100644 --- a/app/graphql/resolvers/releases_resolver.rb +++ b/app/graphql/resolvers/releases_resolver.rb @@ -12,6 +12,8 @@ module Resolvers end def resolve(**args) + return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true) + ReleasesFinder.new( project, current_user diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index f83faa36396..cbc75b5770c 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -243,15 +243,13 @@ module Types Types::ReleaseType.connection_type, null: true, description: 'Releases of the project', - resolver: Resolvers::ReleasesResolver, - feature_flag: :graphql_release_data + resolver: Resolvers::ReleasesResolver field :release, Types::ReleaseType, null: true, description: 'A single release of the project', resolver: Resolvers::ReleasesResolver.single, - feature_flag: :graphql_release_data, authorize: :download_code field :container_expiration_policy, diff --git a/changelogs/unreleased/nfriend-enable-graphql_release_data-feature-flag.yml b/changelogs/unreleased/nfriend-enable-graphql_release_data-feature-flag.yml new file mode 100644 index 00000000000..b56c2337149 --- /dev/null +++ b/changelogs/unreleased/nfriend-enable-graphql_release_data-feature-flag.yml @@ -0,0 +1,5 @@ +--- +title: Add release data to GraphQL endpoint +merge_request: 34937 +author: +type: added diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 772f79c2204..502dac666ee 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -9289,7 +9289,7 @@ type Project { publicJobs: Boolean """ - A single release of the project. Available only when feature flag `graphql_release_data` is enabled + A single release of the project """ release( """ @@ -9299,7 +9299,7 @@ type Project { ): Release """ - Releases of the project. Available only when feature flag `graphql_release_data` is enabled + Releases of the project """ releases( """ diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index e041c5270ca..aecc533931e 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -27364,7 +27364,7 @@ }, { "name": "release", - "description": "A single release of the project. Available only when feature flag `graphql_release_data` is enabled", + "description": "A single release of the project", "args": [ { "name": "tagName", @@ -27391,7 +27391,7 @@ }, { "name": "releases", - "description": "Releases of the project. Available only when feature flag `graphql_release_data` is enabled", + "description": "Releases of the project", "args": [ { "name": "after", diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ab078c0697b..bba6cd98b27 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1353,7 +1353,7 @@ Information about pagination in a connection. | `pipeline` | Pipeline | Build pipeline of the project | | `printingMergeRequestLinkEnabled` | Boolean | Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line | | `publicJobs` | Boolean | Indicates if there is public access to pipelines and job details of the project, including output logs and artifacts | -| `release` | Release | A single release of the project. Available only when feature flag `graphql_release_data` is enabled | +| `release` | Release | A single release of the project | | `removeSourceBranchAfterMerge` | Boolean | Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project | | `repository` | Repository | Git repository of the project | | `requestAccessEnabled` | Boolean | Indicates if users can request member access to the project | diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md new file mode 100644 index 00000000000..62094ffc940 --- /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" : "![dk](uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png)" + } +} +``` diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index e2c042f9c2e..096e36ebf66 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -70,7 +70,28 @@ See the [Structure](styleguide.md#structure) section of the [Documentation Style ## Metadata To provide additional directives or useful information, we add metadata in YAML -format to the beginning of each product documentation page. +format to the beginning of each product documentation page (YAML front matter). +All values are treated as strings and are only used for the +[docs website](site_architecture/index.md). + +### Stage and group metadata + +Each page should ideally have metadata related to the stage and group it +belongs to, as well as an information block as described below: + +- `stage`: The [Stage](https://about.gitlab.com/handbook/product/categories/#devops-stages) + to which the majority of the page's content belongs. +- `group`: The [Group](https://about.gitlab.com/company/team/structure/#product-groups) + to which the majority of the page's content belongs. +- `info`: The following line, which provides direction to contributors regarding + how to contact the Technical Writer associated with the page's Stage and + Group: + + ```plaintext + To determine the technical writer assigned to the Stage/Group + associated with this page, see + https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers + ``` For example, the following metadata would be at the beginning of a product documentation page whose content is primarily associated with the Audit Events @@ -84,24 +105,64 @@ info: To determine the technical writer assigned to the Stage/Group associated w --- ``` -The following list describes the YAML parameters in use: +### Page type metadata + +Originally discussed in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/1280), +each page should have a `type` metadata. It can be one or more of the following: + +- `index`: Index/overview pages. They serve as a list to other pages. Doesn't + necessarily mean the page should be named `index.md`. [Example page](../../install/README.md). +- `concepts`: What you need to know before using product. Informational, not + instructional. For example, abstract ideas, explain meaning or benefit, support + understanding of tasks. They are read for background information, for example + "Why X is important". [Example page](../../topics/autodevops/index.md). +- `howto`: Specific use case instructions. [Example page](../../ssh/README.md). +- `tutorial`: Learn a process/concept by doing. [Example page](../../gitlab-basics/start-using-git.md). +- `reference`: Covers what things are/do. Things like specific settings, facts + without too much explanation that are read for detailed information. + [Example page](../../ci/yaml/README.md). + +### Redirection metadata + +The following metadata should be added when a page is moved to another location: - `redirect_to`: The relative path and filename (with an `.md` extension) of the location to which visitors should be redirected for a moved page. [Learn more](#changing-document-location). -- `stage`: The [Stage](https://about.gitlab.com/handbook/product/categories/#devops-stages) - to which the majority of the page's content belongs. -- `group`: The [Group](https://about.gitlab.com/company/team/structure/#product-groups) - to which the majority of the page's content belongs. -- `info`: The following line, which provides direction to contributors regarding - how to contact the Technical Writer associated with the page's Stage and - Group: `To determine the technical writer assigned to the Stage/Group - associated with this page, see - https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers` - `disqus_identifier`: Identifier for Disqus commenting system. Used to keep comments with a page that's been moved to a new URL. [Learn more](#redirections-for-pages-with-disqus-comments). +### Comments metadata + +The [docs website](site_architecture/index.md) has comments (provided by Disqus) +enabled by default. In case you want to disable them (for example in index pages), +set it to `false`: + +```yaml +--- +comments: false +--- +``` + +### Additional page metadata + +Each page can have additional (optional) metadata (set in the +[default.html](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/fc3577921343173d589dfa43d837b4307e4e620f/layouts/default.html#L30-52) +Nanoc layout), which will be shown to the top of the page if defined: + +- `author`: The name of the author of a page, usually a tutorial. It requires `author_gitlab` in order to be shown. +- `author_gitlab`: The username of the author on GitLab.com. It requires `author` in order to be shown. +- `date`: The date the page was created, usually for tutorials. +- `article_type`: The type of article. Can be either `tutorial` or `user guide`. +- `level`: The level of complexity of a how-to or tutorial. Can be either `beginner`, + `advanced`, or `intermediate`. +- `last_updated`: The date in ISO format when the page was last updated. For example `2020-02-14`. +- `reading_time`: If you want to add an indication of the approximate reading + time of a page, you can set `reading_time` to `true`. This uses a simple + [algorithm](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/lib/helpers/reading_time.rb) + to calculate the reading time based on the number of words. + ## Changing document location Changing a document's location requires specific steps to ensure that diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index abcd1afacb4..b10136a6ffe 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -876,8 +876,6 @@ Inside the document: - Always use a proper description for what the image is about. That way, when a browser fails to show the image, this text will be used as an alternative description. -- If a heading is placed right after an image, always add three dashes (`---`) - between the image and the heading. ### Remove image shadow @@ -1336,6 +1334,22 @@ a helpful link back to how the feature was developed. > - [Moved](<link-to-issue>) to GitLab Core in 12.0. ``` +- If a feature is deprecated, include a link to a replacement (when available): + + ```markdown + > - [Deprecated](<link-to-issue>) in GitLab 11.3. Replaced by [meaningful text](<link-to-appropriate-documentation>). + ``` + + It's also acceptable to describe the replacement in surrounding text, if available. + + If the deprecation is not obvious in existing text, you may want to include a warning such as: + + ```markdown + CAUTION: **Warning:** + This feature was [deprecated](link-to-issue) in GitLab 12.3 + and replaced by [Feature name](link-to-feature-documentation). + ``` + NOTE: **Note:** Version text must be on its own line and surrounded by blank lines to render correctly. diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md index 33fd468140f..d922bd85bd0 100644 --- a/doc/development/integrations/secure.md +++ b/doc/development/integrations/secure.md @@ -239,17 +239,15 @@ one set in the `SECURE_LOG_LEVEL` variable. For instance, `info` and `warn` messages should be skipped when `SECURE_LOG_LEVEL` is set to `error`. Accepted values are as follows, listed from highest to lowest: -- `panic` - `fatal` - `error` - `warn` - `info` - `debug` -- `trace` -It is recommended to use the `debug` and `trace` levels for verbose logging -that could be useful when debugging. The default value for `SECURE_LOG_LEVEL` -should be set to `info`. +It is recommended to use the `debug` level for verbose logging that could be +useful when debugging. The default value for `SECURE_LOG_LEVEL` should be set +to `info`. #### common logutil package diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index 4bf1c6ae1c6..99feacd821b 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -174,7 +174,7 @@ using environment variables. | `CLAIR_DB_IMAGE_TAG` | (**DEPRECATED - use `CLAIR_DB_IMAGE` instead**) The Docker image tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes. | `latest` | | `DOCKERFILE_PATH` | The path to the `Dockerfile` to be used for generating remediations. By default, the scanner will look for a file named `Dockerfile` in the root directory of the project, so this variable should only be configured if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | `Dockerfile` | | `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs that you want to trust. | "" | -| `SECURE_LOG_LEVEL` | The log levels available are: `panic`, `fatal`, `error`, `warn`, `info`, `debug`, `trace` | `info` | +| `SECURE_LOG_LEVEL` | The log levels available are: `fatal`, `error`, `warn`, `info`, `debug` | `info` | ### Overriding the Container Scanning template diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 65aa145da60..c462faf3a64 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -155,7 +155,7 @@ The following variables allow configuration of global dependency scanning settin | `DS_DISABLE_DIND` | Disable Docker-in-Docker and run analyzers [individually](#enabling-docker-in-docker). This variable is `true` by default. | | `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs to trust. | | `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"` | -| `SECURE_LOG_LEVEL` | Default log level is `info`, you can set it to any of the following strings: `panic`, `fatal`, `error`, `warn`, `info`, `debug`, `trace`. | +| `SECURE_LOG_LEVEL` | Default log level is `info`, you can set it to any of the following strings: `fatal`, `error`, `warn`, `info`, `debug`. | #### Configuring Docker-in-Docker orchestrator diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md index b70272515a7..d612850f650 100644 --- a/doc/user/application_security/sast/index.md +++ b/doc/user/application_security/sast/index.md @@ -289,13 +289,11 @@ SAST can be [configured](#customizing-the-sast-settings) using environment varia You can control the verbosity of logs by setting the `SECURE_LOG_LEVEL` env var. The default is set to `info`, you can set it to any of the following levels: -- `panic` - `fatal` - `error` - `warn` - `info` - `debug` -- `trace` #### Custom Certificate Authority @@ -320,13 +318,13 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre | Environment variable | Default value | Description | |-------------------------|---------------|-------------| | `SAST_EXCLUDED_PATHS` | `spec, test, tests, tmp` | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec` ). Parent directories will also match patterns. | -| `SAST_BANDIT_EXCLUDED_PATHS` | - | comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html); For example: `'*/tests/*'` | +| `SAST_BANDIT_EXCLUDED_PATHS` | | comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html); For example: `'*/tests/*'` | | `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. | | `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. | | `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. | | `SAST_GOSEC_LEVEL` | 0 | Ignore Gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. | -| `SAST_GITLEAKS_COMMIT_FROM` | - | The commit a Gitleaks scan starts at. | -| `SAST_GITLEAKS_COMMIT_TO` | - | The commit a Gitleaks scan ends at. | +| `SAST_GITLEAKS_COMMIT_FROM` | | The commit a Gitleaks scan starts at. | +| `SAST_GITLEAKS_COMMIT_TO` | | The commit a Gitleaks scan ends at. | | `SAST_GITLEAKS_HISTORIC_SCAN` | false | Flag to enable a historic Gitleaks scan. | #### Docker-in-Docker orchestrator diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md index c47d4ae9996..b0bac0095a6 100644 --- a/doc/user/application_security/secret_detection/index.md +++ b/doc/user/application_security/secret_detection/index.md @@ -150,13 +150,11 @@ Secret Detection can be customized by defining available variables: You can control the verbosity of logs by setting the `SECURE_LOG_LEVEL` env var. The default is set to `info`, you can set it to any of the following levels: -- `panic` - `fatal` - `error` - `warn` - `info` - `debug` -- `trace` ## Full History Secret Scan diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 0d028cfe77a..999c49cbc3c 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -502,8 +502,6 @@ Second section content. ![Preview of an auto-generated TOC in a Wiki](img/markdown_toc_preview_v12_9.png) ---- - ### Wiki-specific Markdown The following examples show how links inside wikis behave. diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 89b17609698..1831563332f 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -42,8 +42,6 @@ Check all the [GitLab Enterprise features for issue boards](#gitlab-enterprise-f ![GitLab issue boards - Premium](img/issue_boards_premium.png) ---- - ## How it works The Issue Board feature builds on GitLab's existing @@ -98,8 +96,6 @@ If you have the labels "**backend**", "**frontend**", "**staging**", and ![issue card moving](img/issue_board_move_issue_card_list.png) ---- - ### Use cases for multiple issue boards With [multiple issue boards](#multiple-issue-boards), @@ -252,8 +248,6 @@ clicking **View scope**. ![Viewing board configuration](img/issue_board_view_scope.png) ---- - ### Focus mode > - [Introduced]((https://about.gitlab.com/releases/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep)) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.1. @@ -275,8 +269,6 @@ especially in combination with [assignee lists](#assignee-lists-premium). ![issue board summed weights](img/issue_board_summed_weights.png) ---- - ### Group issue boards **(PREMIUM)** > [Introduced](https://about.gitlab.com/releases/2017/09/22/gitlab-10-0-released/#group-issue-boards) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0. @@ -292,8 +284,6 @@ Multiple group issue boards were originally [introduced](https://about.gitlab.co ![Group issue board](img/group_issue_board.png) ---- - ### Assignee lists **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5784) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.0. @@ -313,8 +303,6 @@ To remove an assignee list, just as with a label list, click the trash icon. ![Assignee lists](img/issue_board_assignee_lists.png) ---- - ### Milestone lists **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6469) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2. @@ -332,8 +320,6 @@ As in other list types, click the trash icon to remove a list. ![Milestone lists](img/issue_board_milestone_lists.png) ---- - ## Work In Progress limits **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11403) in GitLab 12.7 @@ -365,8 +351,6 @@ status. ![Blocked issues](img/issue_boards_blocked_icon_v12_8.png) ---- - ## Actions you can take on an issue board - [Create a new list](#create-a-new-list). @@ -437,8 +421,6 @@ the list by filtering by author, assignee, milestone, and label. ![Bulk adding issues to lists](img/issue_boards_add_issues_modal.png) ---- - ### Remove an issue from a list Removing an issue from a list can be done by clicking the issue card and then @@ -447,8 +429,6 @@ respective label is removed. ![Remove issue from list](img/issue_boards_remove_issue.png) ---- - ### Filter issues You should be able to use the filters on top of your issue board to show only @@ -492,8 +472,6 @@ to another list the label changes and a system not is recorded. ![issue board system notes](img/issue_board_system_notes.png) ---- - ### Drag issues between lists When dragging issues between lists, different behavior occurs depending on the source list and the target list. @@ -518,8 +496,6 @@ To select and move multiple cards: ![Multi-select Issue Cards](img/issue_boards_multi_select_v12_4.png) ---- - ### Issue ordering in a list When visiting a board, issues appear ordered in any list. You're able to change diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md index 06a80672769..9113f5344ba 100644 --- a/doc/user/project/issues/index.md +++ b/doc/user/project/issues/index.md @@ -175,8 +175,6 @@ requires [GraphQL](../../../api/graphql/index.md) to be enabled. ![Similar issues](img/similar_issues.png) ---- - ### Health status **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36427) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10. diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index b1bbc3cf0fd..d8569dea4f1 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -79,12 +79,6 @@ module API @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 00000000000..49da1e317ab --- /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 c1bf3a64923..006dc257fe1 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 @@ module API 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/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index bd3e5b5c340..f9c19d9747d 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -354,4 +354,21 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do end end end + + describe 'ensures that the release data can be contolled by a feature flag' do + context 'when the graphql_release_data feature flag is disabled' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:release) { create(:release, project: project) } + + let(:current_user) { developer } + + before do + stub_feature_flags(graphql_release_data: false) + + project.add_developer(developer) + end + + it_behaves_like 'no access to the release field' + end + end end diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb index 0d20acad279..9d0387e6469 100644 --- a/spec/requests/api/graphql/project/releases_spec.rb +++ b/spec/requests/api/graphql/project/releases_spec.rb @@ -264,4 +264,21 @@ describe 'Query.project(fullPath).releases()' do end end end + + describe 'ensures that the release data can be contolled by a feature flag' do + context 'when the graphql_release_data feature flag is disabled' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:release) { create(:release, project: project) } + + let(:current_user) { developer } + + before do + stub_feature_flags(graphql_release_data: false) + + project.add_developer(developer) + end + + it_behaves_like 'no access to any release data' + end + end end diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index ea22e442127..f271f8aa853 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -21,178 +21,10 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -238,7 +70,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do get api(url, user) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -331,7 +163,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -453,7 +285,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do project.add_maintainer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end end @@ -481,7 +313,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do project.add_maintainer(user) end - include_examples 'creates wiki page' + include_examples 'wikis API creates wiki page' end end end @@ -515,7 +347,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do put(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -547,7 +379,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do delete(api(url, user)) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -686,7 +518,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do delete(api(url, user)) end - include_examples '204 No Content' + include_examples 'wiki API 204 No Content' end end @@ -718,7 +550,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do delete(api(url, user)) end - include_examples '204 No Content' + include_examples 'wiki API 204 No Content' end end @@ -783,7 +615,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do post(api(url, user), params: payload) end - include_examples '403 Forbidden' + include_examples 'wiki API 403 Forbidden' end end @@ -813,7 +645,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do project.add_maintainer(user) end - include_examples 'uploads wiki attachment' + include_examples 'wiki API uploads wiki attachment' end end @@ -841,7 +673,7 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 @@ RSpec.describe API::Wikis do 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 00000000000..2e4c667d37e --- /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 |