Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-13 00:10:14 +00:00
parent 3b69a04945
commit 7e87cfd402
36 changed files with 1494 additions and 323 deletions

View File

@ -386,7 +386,7 @@ export default {
data-testid="aws-guidance-tip"
@dismiss="dismissTip"
>
<div class="gl-display-flex gl-flex-direction-row">
<div class="gl-display-flex gl-flex-direction-row gl-flex-wrap-wrap gl-md-flex-wrap-nowrap">
<div>
<p>
<gl-sprintf :message="$options.awsTipMessage">

View File

@ -1,5 +1,6 @@
<script>
import { GlTabs, GlTab } from '@gitlab/ui';
import { isString } from 'lodash';
import { __ } from '~/locale';
import GroupsStore from '../store/groups_store';
import GroupsService from '../service/groups_service';
@ -17,7 +18,7 @@ export default {
return {
tabs: [
{
title: this.$options.i18n.subgroupsAndProjects,
title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
renderEmptyState: true,
lazy: false,
@ -25,7 +26,7 @@ export default {
store: new GroupsStore({ showSchemaMarkup: true }),
},
{
title: this.$options.i18n.sharedProjects,
title: this.$options.i18n[ACTIVE_TAB_SHARED],
key: ACTIVE_TAB_SHARED,
renderEmptyState: false,
lazy: true,
@ -33,7 +34,7 @@ export default {
store: new GroupsStore(),
},
{
title: this.$options.i18n.archivedProjects,
title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
key: ACTIVE_TAB_ARCHIVED,
renderEmptyState: false,
lazy: true,
@ -44,18 +45,40 @@ export default {
activeTabIndex: 0,
};
},
mounted() {
const activeTabIndex = this.tabs.findIndex((tab) => tab.key === this.$route.name);
if (activeTabIndex === -1) {
return;
}
this.activeTabIndex = activeTabIndex;
},
methods: {
handleTabInput(tabIndex) {
if (tabIndex === this.activeTabIndex) {
return;
}
this.activeTabIndex = tabIndex;
const tab = this.tabs[tabIndex];
tab.lazy = false;
// Vue router will convert `/` to `%2F` if you pass a string as a param
// If you pass an array as a param it will concatenate them with a `/`
// This makes sure we are always passing an array for the group param
const groupParam = isString(this.$route.params.group)
? this.$route.params.group.split('/')
: this.$route.params.group;
this.$router.push({ name: tab.key, params: { group: groupParam } });
},
},
i18n: {
subgroupsAndProjects: __('Subgroups and projects'),
sharedProjects: __('Shared projects'),
archivedProjects: __('Archived projects'),
[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]: __('Subgroups and projects'),
[ACTIVE_TAB_SHARED]: __('Shared projects'),
[ACTIVE_TAB_ARCHIVED]: __('Archived projects'),
},
};
</script>

View File

@ -1,4 +1,5 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import { GlToast } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils';
import GroupFolder from './components/group_folder.vue';
@ -10,6 +11,22 @@ import {
} from './constants';
import OverviewTabs from './components/overview_tabs.vue';
export const createRouter = () => {
const routes = [
{ name: ACTIVE_TAB_SHARED, path: '/groups/:group*/-/shared' },
{ name: ACTIVE_TAB_ARCHIVED, path: '/groups/:group*/-/archived' },
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, path: '/:group*' },
];
const router = new VueRouter({
routes,
mode: 'history',
base: '/',
});
return router;
};
export const initGroupOverviewTabs = () => {
const el = document.getElementById('js-group-overview-tabs');
@ -18,6 +35,9 @@ export const initGroupOverviewTabs = () => {
Vue.component('GroupFolder', GroupFolder);
Vue.component('GroupItem', GroupItem);
Vue.use(GlToast);
Vue.use(VueRouter);
const router = createRouter();
const {
newSubgroupPath,
@ -35,6 +55,7 @@ export const initGroupOverviewTabs = () => {
return new Vue({
el,
router,
provide: {
newSubgroupPath,
newProjectPath,

View File

@ -349,3 +349,9 @@
}
}
}
.gl-md-flex-wrap-nowrap.gl-md-flex-wrap-nowrap {
@include gl-media-breakpoint-up(md) {
@include gl-flex-wrap-nowrap;
}
}

View File

@ -978,6 +978,9 @@ svg {
.gl-flex-wrap {
flex-wrap: wrap;
}
.gl-justify-content-center {
justify-content: center;
}
.gl-float-right {
float: right;
}

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
module Resolvers
module Environments
class LastDeploymentResolver < BaseResolver
argument :status,
Types::DeploymentStatusEnum,
required: true,
description: 'Status of the Deployment.'
type Types::DeploymentType, null: true
def resolve(status:)
return unless object.present? && object.is_a?(::Environment)
validate!(status)
find_last_deployment(status)
end
private
def find_last_deployment(status)
BatchLoader::GraphQL.for(object).batch(key: status) do |environments, loader, args|
association_name = "last_#{args[:key]}_deployment".to_sym
Preloaders::Environments::DeploymentPreloader.new(environments)
.execute_with_union(association_name, {})
environments.each do |environment|
loader.call(environment, environment.public_send(association_name)) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
def validate!(status)
unless Deployment::FINISHED_STATUSES.include?(status.to_sym) ||
Deployment::UPCOMING_STATUSES.include?(status.to_sym)
raise Gitlab::Graphql::Errors::ArgumentError, "\"#{status}\" status is not supported."
end
end
end
end
end

View File

@ -21,8 +21,8 @@ module Resolvers
def resolve(**args)
return unless project.present?
Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
rescue Environments::EnvironmentsFinder::InvalidStatesError => e
::Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
rescue ::Environments::EnvironmentsFinder::InvalidStatesError => e
raise Gitlab::Graphql::Errors::ArgumentError, e.message
end
end

View File

@ -63,6 +63,11 @@ module Types
resolver: Resolvers::DeploymentsResolver,
complexity: 150
field :last_deployment,
Types::DeploymentType,
description: 'Last deployment of the environment.',
resolver: Resolvers::Environments::LastDeploymentResolver
def tier
object.tier.to_sym
end

View File

@ -59,6 +59,7 @@ class Deployment < ApplicationRecord
scope :finished_before, ->(date) { where('finished_at < ?', date) }
scope :ordered, -> { order(finished_at: :desc) }
scope :ordered_as_upcoming, -> { order(id: :desc) }
VISIBLE_STATUSES = %i[running success failed canceled blocked].freeze
FINISHED_STATUSES = %i[success failed canceled].freeze

View File

@ -30,6 +30,16 @@ class Environment < ApplicationRecord
has_one :last_deployment, -> { success.ordered }, class_name: 'Deployment', inverse_of: :environment
has_one :last_visible_deployment, -> { visible.order(id: :desc) }, inverse_of: :environment, class_name: 'Deployment'
Deployment::FINISHED_STATUSES.each do |status|
has_one :"last_#{status}_deployment", -> { where(status: status).ordered },
class_name: 'Deployment', inverse_of: :environment
end
Deployment::UPCOMING_STATUSES.each do |status|
has_one :"last_#{status}_deployment", -> { where(status: status).ordered_as_upcoming },
class_name: 'Deployment', inverse_of: :environment
end
has_one :upcoming_deployment, -> { upcoming.order(id: :desc) }, class_name: 'Deployment', inverse_of: :environment
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment

View File

@ -1198,7 +1198,7 @@ class MergeRequest < ApplicationRecord
skip_ci_check: skip_ci_check,
skip_discussions_check: skip_discussions_check
})
additional_checks.execute.success?
additional_checks.success?
end
def ff_merge_possible?

View File

@ -1,19 +1,20 @@
- hide_remember_me = local_assigns.fetch(:hide_remember_me, false)
%div{ class: Feature.enabled?(:restyle_login_page, @project) ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
%label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
- restyle_login_page_enabled = Feature.enabled?(:restyle_login_page, @project)
%div{ class: restyle_login_page_enabled ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
%label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
= _('Sign in with')
- providers = enabled_button_based_providers
.gl-display-flex.gl-justify-content-between.gl-flex-wrap
.gl-display-flex.gl-flex-wrap{ class: restyle_login_page_enabled ? 'gl-justify-content-center' : 'gl-justify-content-between' }
- providers.each do |provider|
- has_icon = provider_has_icon?(provider)
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' if Feature.disabled?(:restyle_login_page, @project)}", form: { class: 'gl-w-full gl-mb-3' } do
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' unless restyle_login_page_enabled}", form: { class: restyle_login_page_enabled ? 'gl-mb-3' : 'gl-w-full gl-mb-3' } do
- if has_icon
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)
- unless hide_remember_me
%fieldset
%label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : '' }
%label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : '' }
= check_box_tag :remember_me, nil, false
%span
= _('Remember me')

View File

@ -1,18 +1,18 @@
- text_style = 'font-size:16px; text-align:center; line-height:30px;'
%p{ style: text_style }
Your CSV import for project
%a{ href: project_url(@project), style: "color:#3777b0; text-decoration:none;" }
= @project.full_name
has been completed.
- project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none;")
= s_('Notify|Your CSV import for project %{project_link} has been completed.').html_safe % { project_link: project_link }
%p{ style: text_style }
#{pluralize(@results[:success], 'issue')} imported.
- issues = n_('%d issue', '%d issues', @results[:success]) % @results[:success]
= s_('Notify|%{issues} imported.') % { issues: issues }
- if @results[:error_lines].present?
%p{ style: text_style }
Errors found on line #{'number'.pluralize(@results[:error_lines].size)}: #{@results[:error_lines].join(', ')}. Please check if these lines have an issue title.
= s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title.') % { singular_or_plural_line: n_('line', 'lines', @results[:error_lines].size),
error_lines: @results[:error_lines].join(', ') }
- if @results[:parse_error]
%p{ style: text_style }
Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.
= s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.')

View File

@ -0,0 +1,11 @@
- name: "Starboard directive in the config for the GitLab Agent for Kubernetes"
announcement_milestone: "15.4"
announcement_date: "2022-09-22"
removal_milestone: "16.0"
removal_date: "2023-05-22"
breaking_change: true
reporter: sam.white
stage: secure
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368828
body: |
GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.

View File

@ -11692,6 +11692,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="environmentdeploymentsorderby"></a>`orderBy` | [`DeploymentsOrderByInput`](#deploymentsorderbyinput) | Order by a specified field. |
| <a id="environmentdeploymentsstatuses"></a>`statuses` | [`[DeploymentStatus!]`](#deploymentstatus) | Statuses of the deployments. |
##### `Environment.lastDeployment`
Last deployment of the environment.
Returns [`Deployment`](#deployment).
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="environmentlastdeploymentstatus"></a>`status` | [`DeploymentStatus!`](#deploymentstatus) | Status of the Deployment. |
##### `Environment.metricsDashboard`
Metrics dashboard schema for the environment.

View File

@ -86,6 +86,20 @@ default is applied:
- On GitLab.com during the 16.0 milestone.
- On GitLab self-managed instances when they are upgraded to 16.0.
</div>
<div class="deprecation removal-160 breaking-change">
### Starboard directive in the config for the GitLab Agent for Kubernetes
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.
</div>
</div>

View File

@ -344,34 +344,264 @@ For details of API fuzzing configuration options, see [Available CI/CD variables
#### Postman variables
> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
##### Variables in Postman Client
Postman allows the developer to define placeholders that can be used in different parts of the
requests. These placeholders are called variables, as explained in the Postman documentation,
[Using variables](https://learning.postman.com/docs/sending-requests/variables/).
requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
You can use variables to store and reuse values in your requests and scripts. For example, you can
edit the collection to add variables to the document:
![Edit collection variable tab View](img/api_fuzzing_postman_collection_edit_variable.png)
Or alternatively, you can add variables in an environment:
![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
You can then use the variables in sections such as URL, headers, and others:
![Edit request using variables View](img/api_fuzzing_postman_request_edit.png)
Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
the Environment scope:
Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
When you export a Postman collection, only Postman collection variables are exported into the
Postman file. For example, Postman does not export environment-scoped variables into the Postman
file.
Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
By default, the API fuzzer uses the Postman file to resolve Postman variable values. If a JSON file
is set in a GitLab CI/CD variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`, then the JSON
file takes precedence to get Postman variable values.
As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
WARNING:
Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`.
> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
The following is a summary of the variable scopes supported by the Postman Client and API Fuzzing:
- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with API Fuzzing.
- **Environment scope** is a named group of variables created by a user in the Postman Client.
The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with API Fuzzing.
- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
- **API Fuzzing Scope** is a new scope added by API Fuzzing to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _API Fuzzing Scope_ variables are provided using a [custom JSON file format](#api-fuzzing-scope-custom-json-file-format).
- Override values defined in the environment or collection
- Defining variables from scripts
- Define a single row of data from the unsupported _data scope_
- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
API Fuzzing does **not** support reading data from a CSV or JSON file.
- **Local scope** are variables that are defined in Postman scripts. API Fuzzing does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
Not all scopes are supported by API Fuzzing and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
| Scope |Postman | API Fuzzing | Comment |
| ------------------ |:---------:|:------------:| :--------|
| Global Environment | Yes | Yes | Special pre-defined environment |
| Environment | Yes | Yes | Named environments |
| Collection | Yes | Yes | Defined in your postman collection |
| API Fuzzing Scope | No | Yes | Custom scope added by API Fuzzing |
| Data | Yes | No | External files in CSV or JSON format |
| Local | Yes | No | Variables defined in scripts |
For more details on how to define variables and export variables in different scopes, see:
- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
##### Exporting from Postman Client
The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
For more details on exporting variables in different supported scopes, see:
- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
##### API Fuzzing Scope, custom JSON file format
Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
This example defines two variables `base_url` and `token` in the API Fuzzing scope:
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
##### Using scopes with API Fuzzing
The scopes: _global_, _environment_, _collection_, and _GitLab API Fuzzing_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab API Fuzzing_ scopes.
The following table provides a quick reference for mapping scope files/URLs to API Fuzzing configuration variables:
| Scope | How to Provide |
| ------------------ | --------------- |
| Global Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
| Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
| Collection | FUZZAPI_POSTMAN_COLLECTION |
| API Fuzzing Scope | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
| Data | Not supported |
| Local | Not supported |
The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `FUZZAPI_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
Variables from other scopes are provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
The configuration variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` can be set to:
- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
- [API Fuzzing Custom JSON format](#api-fuzzing-scope-custom-json-file-format)
##### Undefined Postman variables
There is a chance that API Fuzzing engine does not find all variables references that your Postman collection file is using. Some cases can be:
- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by API Fuzzing. Thus, assuming the values for these variables have not been provided through [the API Fuzzing scope](#api-fuzzing-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
- A variable name was typed incorrectly, and the name does not match the defined variable.
- Postman Client supports a new dynamic variable that is not supported by API Fuzzing.
When possible, API Fuzzing follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
##### Dynamic Postman variables
In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
An important difference between API Fuzzing and Postman is that API Fuzzing returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, API Fuzzing uses static values for dynamic variables while Postman uses random values.
The supported dynamic variables during the scanning process are:
| Variable | Value |
| ----------- | ----------- |
| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
| `$randomAbbreviation` | `PCI` |
| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
| `$randomAdjective` | `auxiliary` |
| `$randomAlphaNumeric` | `a` |
| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
| `$randomBankAccount` | `09454073` |
| `$randomBankAccountBic` | `EZIAUGJ1` |
| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
| `$randomBankAccountName` | `Home Loan Account` |
| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
| `$randomBoolean` | `true` |
| `$randomBs` | `killer leverage schemas` |
| `$randomBsAdjective` | `viral` |
| `$randomBsBuzz` | `repurpose` |
| `$randomBsNoun` | `markets` |
| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
| `$randomCatchPhraseAdjective` | `Business-focused` |
| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
| `$randomCatchPhraseNoun` | `superstructure` |
| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
| `$randomCity` | `Spinkahaven` |
| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
| `$randomColor` | `fuchsia` |
| `$randomCommonFileExt` | `wav` |
| `$randomCommonFileName` | `well_modulated.mpg4` |
| `$randomCommonFileType` | `audio` |
| `$randomCompanyName` | `Grady LLC` |
| `$randomCompanySuffix` | `Inc` |
| `$randomCountry` | `Kazakhstan` |
| `$randomCountryCode` | `MD` |
| `$randomCreditCardMask` | `3622` |
| `$randomCurrencyCode` | `ZMK` |
| `$randomCurrencyName` | `Pound Sterling` |
| `$randomCurrencySymbol` | `£` |
| `$randomDatabaseCollation` | `utf8_general_ci` |
| `$randomDatabaseColumn` | `updatedAt` |
| `$randomDatabaseEngine` | `Memory` |
| `$randomDatabaseType` | `text` |
| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
| `$randomDepartment` | `Electronics` |
| `$randomDirectoryPath` | `/usr/local/bin` |
| `$randomDomainName` | `trevor.info` |
| `$randomDomainSuffix` | `org` |
| `$randomDomainWord` | `jaden` |
| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
| `$randomExampleEmail` | `non-a-valid-user@example.net` |
| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
| `$randomFileExt` | `war` |
| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
| `$randomFilePath` | `/home/programming_chicken.cpio` |
| `$randomFileType` | `application` |
| `$randomFirstName` | `Chandler` |
| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
| `$randomFullName` | `Connie Runolfsdottir` |
| `$randomHexColor` | `#47594a` |
| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
| `$randomIngverb` | `navigating` |
| `$randomInt` | `494` |
| `$randomIP` | `241.102.234.100` |
| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
| `$randomJobArea` | `Mobility` |
| `$randomJobDescriptor` | `Senior` |
| `$randomJobTitle` | `International Creative Liaison` |
| `$randomJobType` | `Supervisor` |
| `$randomLastName` | `Schneider` |
| `$randomLatitude` | `55.2099` |
| `$randomLocale` | `ny` |
| `$randomLongitude` | `40.6609` |
| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
| `$randomLoremWord` | `est` |
| `$randomLoremWords` | `vel repellat nobis` |
| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
| `$randomMonth` | `February` |
| `$randomNamePrefix` | `Dr.` |
| `$randomNameSuffix` | `MD` |
| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
| `$randomNoun` | `bus` |
| `$randomPassword` | `t9iXe7COoDKv8k3` |
| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
| `$randomPhoneNumber` | `700-008-5275` |
| `$randomPhoneNumberExt` | `27-199-983-3864` |
| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
| `$randomPrice` | `531.55` |
| `$randomProduct` | `Pizza` |
| `$randomProductAdjective` | `Unbranded` |
| `$randomProductMaterial` | `Steel` |
| `$randomProductName` | `Handmade Concrete Tuna` |
| `$randomProtocol` | `https` |
| `$randomSemver` | `7.0.5` |
| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
| `$randomStreetAddress` | `5742 Harvey Streets` |
| `$randomStreetName` | `Kuhic Island` |
| `$randomTransactionType` | `payment` |
| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
| `$randomUrl` | `https://no-a-valid-host.net` |
| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
| `$randomUserName` | `Jarrell.Gutkowski` |
| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
| `$randomVerb` | `navigate` |
| `$randomWeekday` | `Thursday` |
| `$randomWord` | `withdrawal` |
| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
| `$timestamp` | `1562757107` |
##### Example: Global Scope
In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
@ -384,21 +614,171 @@ include:
variables:
FUZZAPI_PROFILE: Quick-10
FUZZAPI_POSTMAN_COLLECTION: postman-collection_serviceA.json
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
##### Example: Environment Scope
In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
##### Example: Collection Scope
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION`:
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_TARGET_URL: http://test-deployment/
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
```
The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
key-value pairs for properties. The keys are the variables' names, and the values are the variables'
##### Example: API Fuzzing Scope
The API Fuzzing Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by API Fuzzing, and changing the value of an existing variable defined in another scope. The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
##### Example: Multiple Scopes
In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
##### Example: Changing a Variables Value
When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the API Fuzzing scope can be used to change its value. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"api_version": "v1"
}
```
Our CI definition:
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
##### Example: Changing a Variables Value with Multiple Scopes
When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the API Fuzzing scope can be used. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _API Fuzzing_ scope are configured. The first step is to export and create our various scopes.
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
The API Fuzzing scope is used by creating a file `api-fuzzing-scope.json` using our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"api_version": "v1"
}
```
The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
```yaml
stages:
- fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
variables:
FUZZAPI_PROFILE: Quick
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,api-fuzzing-scope.json
FUZZAPI_TARGET_URL: http://test-deployment/
```
## API fuzzing configuration
@ -420,7 +800,7 @@ provide a script that performs an authentication flow or calculates the token.
#### HTTP Basic Authentication
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
is an authentication method built in to the HTTP protocol and used in conjunction with
is an authentication method built into the HTTP protocol and used in conjunction with
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
@ -499,7 +879,7 @@ Follow these steps to provide the bearer token with `FUZZAPI_OVERRIDES_ENV`:
##### Token generated at test runtime
If the bearer token must be generated and doesn't expire during testing, you can provide to API
fuzzing a file containing the token. A prior stage and job, or part of the API fuzzing job, can
fuzzing with a file containing the token. A prior stage and job, or part of the API fuzzing job, can
generate this file.
API fuzzing expects to receive a JSON file with the following structure:
@ -612,6 +992,7 @@ profile increases as the number of tests increases.
|[`FUZZAPI_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. |
|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
@ -1718,7 +2099,7 @@ For more information, see [Offline environments](../offline_deployments/index.md
### `Error waiting for API Security 'http://127.0.0.1:5000' to become available`
A bug exists in versions of the API Fuzzing analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the DAST API analyzer.
A bug exists in versions of the API Fuzzing analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the API Fuzzing analyzer.
The version information can be found in the job details for the `apifuzzer_fuzz` job.
@ -1835,7 +2216,7 @@ TODO
### Use OpenAPI with an invalid schema
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Fuzzing is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
#### Edit a non-compliant OpenAPI file
@ -1852,7 +2233,7 @@ If your OpenAPI document is generated manually, load your document in the editor
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
API Fuzzing can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Fuzzing analyzer to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
```yaml
stages:
@ -1870,7 +2251,7 @@ variables:
### `No operation in the OpenAPI document is consuming any supported media type`
API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
API Fuzzing uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
**Error message**

View File

@ -267,35 +267,266 @@ This is a minimal configuration for DAST API. From here you can:
- [Add authentication](#authentication).
- Learn how to [handle false positives](#handling-false-positives).
##### Postman variables
#### Postman variables
> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
##### Variables in Postman Client
Postman allows the developer to define placeholders that can be used in different parts of the
requests. These placeholders are called variables, as explained in [Using variables](https://learning.postman.com/docs/sending-requests/variables/).
requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
You can use variables to store and reuse values in your requests and scripts. For example, you can
edit the collection to add variables to the document:
![Edit collection variable tab View](img/dast_api_postman_collection_edit_variable.png)
Or alternatively, you can add variables in an environment:
![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
You can then use the variables in sections such as URL, headers, and others:
![Edit request using variables View](img/dast_api_postman_request_edit.png)
Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
the Environment scope:
Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
When you export a Postman collection, only Postman collection variables are exported into the
Postman file. For example, Postman does not export environment-scoped variables into the Postman
file.
Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
By default, the DAST API scanner uses the Postman file to resolve Postman variable values. If a JSON file
is set in a GitLab CI/CD environment variable `DAST_API_POSTMAN_COLLECTION_VARIABLES`, then the JSON
file takes precedence to get Postman variable values.
As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
WARNING:
Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `DAST_API_POSTMAN_COLLECTION_VARIABLES`.
> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
The following is a summary of the variable scopes supported by the Postman Client and DAST API:
- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with DAST API.
- **Environment scope** is a named group of variables created by a user in the Postman Client.
The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with DAST API.
- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
- **DAST API Scope** is a new scope added by DAST API to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _DAST API Scope_ variables are provided using a [custom JSON file format](#dast-api-scope-custom-json-file-format).
- Override values defined in the environment or collection
- Defining variables from scripts
- Define a single row of data from the unsupported _data scope_
- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
DAST API does **not** support reading data from a CSV or JSON file.
- **Local scope** are variables that are defined in Postman scripts. DAST API does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
Not all scopes are supported by DAST API and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
| Scope |Postman | DAST API | Comment |
| ------------------ |:---------:|:------------:| :--------|
| Global Environment | Yes | Yes | Special pre-defined environment |
| Environment | Yes | Yes | Named environments |
| Collection | Yes | Yes | Defined in your postman collection |
| DAST API Scope | No | Yes | Custom scope added by DAST API |
| Data | Yes | No | External files in CSV or JSON format |
| Local | Yes | No | Variables defined in scripts |
For more details on how to define variables and export variables in different scopes, see:
- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
##### Exporting from Postman Client
The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
For more details on exporting variables in different supported scopes, see:
- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
##### DAST API Scope, custom JSON file format
Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
This example defines two variables `base_url` and `token` in the DAST API scope:
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
##### Using scopes with DAST API
The scopes: _global_, _environment_, _collection_, and _GitLab DAST API_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab DAST API_ scopes.
The following table provides a quick reference for mapping scope files/URLs to DAST API configuration variables:
| Scope | How to Provide |
| ------------------ | --------------- |
| Global Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
| Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
| Collection | DAST_API_POSTMAN_COLLECTION |
| DAST API Scope | DAST_API_POSTMAN_COLLECTION_VARIABLES |
| Data | Not supported |
| Local | Not supported |
The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `DAST_API_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
Variables from other scopes are provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
The configuration variable `DAST_API_POSTMAN_COLLECTION_VARIABLES` can be set to:
- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
- [DAST API Custom JSON format](#dast-api-scope-custom-json-file-format)
##### Undefined Postman variables
There is a chance that DAST API engine does not find all variables references that your Postman collection file is using. Some cases can be:
- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by DAST API. Thus, assuming the values for these variables have not been provided through [the DAST API scope](#dast-api-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
- A variable name was typed incorrectly, and the name does not match the defined variable.
- Postman Client supports a new dynamic variable that is not supported by DAST API.
When possible, DAST API follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
##### Dynamic Postman variables
In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
An important difference between DAST API and Postman is that DAST API returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, DAST API uses static values for dynamic variables while Postman uses random values.
The supported dynamic variables during the scanning process are:
| Variable | Value |
| ----------- | ----------- |
| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
| `$randomAbbreviation` | `PCI` |
| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
| `$randomAdjective` | `auxiliary` |
| `$randomAlphaNumeric` | `a` |
| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
| `$randomBankAccount` | `09454073` |
| `$randomBankAccountBic` | `EZIAUGJ1` |
| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
| `$randomBankAccountName` | `Home Loan Account` |
| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
| `$randomBoolean` | `true` |
| `$randomBs` | `killer leverage schemas` |
| `$randomBsAdjective` | `viral` |
| `$randomBsBuzz` | `repurpose` |
| `$randomBsNoun` | `markets` |
| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
| `$randomCatchPhraseAdjective` | `Business-focused` |
| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
| `$randomCatchPhraseNoun` | `superstructure` |
| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
| `$randomCity` | `Spinkahaven` |
| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
| `$randomColor` | `fuchsia` |
| `$randomCommonFileExt` | `wav` |
| `$randomCommonFileName` | `well_modulated.mpg4` |
| `$randomCommonFileType` | `audio` |
| `$randomCompanyName` | `Grady LLC` |
| `$randomCompanySuffix` | `Inc` |
| `$randomCountry` | `Kazakhstan` |
| `$randomCountryCode` | `MD` |
| `$randomCreditCardMask` | `3622` |
| `$randomCurrencyCode` | `ZMK` |
| `$randomCurrencyName` | `Pound Sterling` |
| `$randomCurrencySymbol` | `£` |
| `$randomDatabaseCollation` | `utf8_general_ci` |
| `$randomDatabaseColumn` | `updatedAt` |
| `$randomDatabaseEngine` | `Memory` |
| `$randomDatabaseType` | `text` |
| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
| `$randomDepartment` | `Electronics` |
| `$randomDirectoryPath` | `/usr/local/bin` |
| `$randomDomainName` | `trevor.info` |
| `$randomDomainSuffix` | `org` |
| `$randomDomainWord` | `jaden` |
| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
| `$randomExampleEmail` | `non-a-valid-user@example.net` |
| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
| `$randomFileExt` | `war` |
| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
| `$randomFilePath` | `/home/programming_chicken.cpio` |
| `$randomFileType` | `application` |
| `$randomFirstName` | `Chandler` |
| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
| `$randomFullName` | `Connie Runolfsdottir` |
| `$randomHexColor` | `#47594a` |
| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
| `$randomIngverb` | `navigating` |
| `$randomInt` | `494` |
| `$randomIP` | `241.102.234.100` |
| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
| `$randomJobArea` | `Mobility` |
| `$randomJobDescriptor` | `Senior` |
| `$randomJobTitle` | `International Creative Liaison` |
| `$randomJobType` | `Supervisor` |
| `$randomLastName` | `Schneider` |
| `$randomLatitude` | `55.2099` |
| `$randomLocale` | `ny` |
| `$randomLongitude` | `40.6609` |
| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
| `$randomLoremWord` | `est` |
| `$randomLoremWords` | `vel repellat nobis` |
| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
| `$randomMonth` | `February` |
| `$randomNamePrefix` | `Dr.` |
| `$randomNameSuffix` | `MD` |
| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
| `$randomNoun` | `bus` |
| `$randomPassword` | `t9iXe7COoDKv8k3` |
| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
| `$randomPhoneNumber` | `700-008-5275` |
| `$randomPhoneNumberExt` | `27-199-983-3864` |
| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
| `$randomPrice` | `531.55` |
| `$randomProduct` | `Pizza` |
| `$randomProductAdjective` | `Unbranded` |
| `$randomProductMaterial` | `Steel` |
| `$randomProductName` | `Handmade Concrete Tuna` |
| `$randomProtocol` | `https` |
| `$randomSemver` | `7.0.5` |
| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
| `$randomStreetAddress` | `5742 Harvey Streets` |
| `$randomStreetName` | `Kuhic Island` |
| `$randomTransactionType` | `payment` |
| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
| `$randomUrl` | `https://no-a-valid-host.net` |
| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
| `$randomUserName` | `Jarrell.Gutkowski` |
| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
| `$randomVerb` | `navigate` |
| `$randomWeekday` | `Thursday` |
| `$randomWord` | `withdrawal` |
| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
| `$timestamp` | `1562757107` |
##### Example: Global Scope
In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
@ -308,21 +539,170 @@ include:
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection_serviceA.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
key-value pairs for properties. The keys are the variables' names, and the values are the variables'
##### Example: Environment Scope
In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
##### Example: Collection Scope
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
Here is an example of using `DAST_API_POSTMAN_COLLECTION`:
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_TARGET_URL: http://test-deployment/
```
##### Example: DAST API Scope
The DAST API Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by DAST API, and changing the value of an existing variable defined in another scope. The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
```json
{
"base_url": "http://127.0.0.1/",
"token": "Token 84816165151"
}
```
##### Example: Multiple Scopes
In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
##### Example: Changing a Variables Value
When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the DAST API scope can be used to change its value. This works because the _DAST API_ scope takes precedence over all other scopes.
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"api_version": "v1"
}
```
Our CI definition:
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
##### Example: Changing a Variables Value with Multiple Scopes
When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the DAST API scope can be used. This works because the _DAST API_ scope takes precedence over all other scopes.
In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _DAST API_ scope are configured. The first step is to export and create our various scopes.
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
The DAST API scope is used by creating a file `dast-api-scope.json` using our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
```json
{
"api_version": "v1"
}
```
The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
```yaml
stages:
- dast
include:
- template: DAST-API.gitlab-ci.yml
variables:
DAST_API_PROFILE: Quick
DAST_API_POSTMAN_COLLECTION: postman-collection.json
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,dast-api-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
## Authentication
@ -332,7 +712,7 @@ provide a script that performs an authentication flow or calculates the token.
### HTTP Basic Authentication
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
is an authentication method built in to the HTTP protocol and used in conjunction with
is an authentication method built into the HTTP protocol and used in conjunction with
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
@ -389,7 +769,7 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
can create CI/CD variables from the GitLab projects page at **Settings > CI/CD**, in the
**Variables** section.
Due to the format of `TEST_API_BEARERAUTH` it's not possible to mask the variable.
To mask the token's value, you can create a second variable with the token value's, and define
To mask the token's value, you can create a second variable with the token values, and define
`TEST_API_BEARERAUTH` with the value `{"headers":{"Authorization":"Bearer $MASKED_VARIABLE"}}`.
1. In your `.gitlab-ci.yml` file, set `DAST_API_OVERRIDES_ENV` to the variable you just created:
@ -408,12 +788,12 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
DAST_API_OVERRIDES_ENV: $TEST_API_BEARERAUTH
```
1. To validate that authentication is working, run an DAST API test and review the job logs
1. To validate that authentication is working, run a DAST API test and review the job logs
and the test API's application logs.
#### Token generated at test runtime
If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API a file that has the token. A prior stage and job, or part of the DAST API job, can
If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API with a file that has the token. A prior stage and job, or part of the DAST API job, can
generate this file.
DAST API expects to receive a JSON file with the following structure:
@ -445,7 +825,7 @@ variables:
DAST_API_OVERRIDES_FILE: dast-api-overrides.json
```
To validate that authentication is working, run an DAST API test and review the job logs and
To validate that authentication is working, run a DAST API test and review the job logs and
the test API's application logs.
#### Token has short expiration
@ -559,7 +939,7 @@ can be added, removed, and modified by creating a custom configuration.
|[`DAST_API_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|[`DAST_API_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|[`DAST_API_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract postman variable values. |
|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|[`DAST_API_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|[`DAST_API_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|[`DAST_API_OVERRIDES_CMD`](#overrides) | Overrides command. |
@ -1583,7 +1963,7 @@ The DAST API engine outputs an error message when it cannot establish a connecti
**Error message**
- In [GitLab 13.11 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/323939), `Failed to start scanner session (version header not found).`
- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the API Security server?`.
- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the DAST API server?`.
**Solution**
@ -1645,7 +2025,7 @@ deploy-test-target:
### Use OpenAPI with an invalid schema
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the DAST API is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
#### Edit a non-compliant OpenAPI file
@ -1662,7 +2042,7 @@ If your OpenAPI document is generated manually, load your document in the editor
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
DAST API can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct DAST API to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
```yaml
stages:
@ -1680,7 +2060,7 @@ variables:
### `No operation in the OpenAPI document is consuming any supported media type`
API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
DAST API uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
**Error message**

View File

@ -85,10 +85,10 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
| Elixir (Phoenix) | [Sobelow](https://github.com/nccgroup/sobelow) | 11.1 |
| Go | [Gosec](https://github.com/securego/gosec) | 10.7 |
| Go | [Semgrep](https://semgrep.dev) | 14.4 |
| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Ant, Maven, SBT) |
| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Maven, SBT) |
| Helm Charts | [Kubesec](https://github.com/controlplaneio/kubesec) | 13.1 |
| Java (any build system) | [Semgrep](https://semgrep.dev) | 14.10 |
| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (Ant, SBT) |
| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (SBT) |
| Java (Android) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
| JavaScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 |
| JavaScript | [Semgrep](https://semgrep.dev) | 13.10 |
@ -104,16 +104,16 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
| React | [Semgrep](https://semgrep.dev) | 13.10 |
| Ruby | [brakeman](https://brakemanscanner.org) | 13.9 |
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Gradle, Maven) |
| Swift (iOS) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
| TypeScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.9, [merged](https://gitlab.com/gitlab-org/gitlab/-/issues/36059) with ESLint in 13.2 |
| TypeScript | [Semgrep](https://semgrep.dev) | 13.10 |
1. .NET 4 support is limited. The analyzer runs in a Linux container and does not have access to Windows-specific libraries or features. Use the Semgrep-based scanner if you need .NET 4 support.
1. The SpotBugs-based analyzer supports [Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
1. The SpotBugs-based analyzer supports [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
[Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html),
[Grails](https://grails.org/),
and the [Maven wrapper](https://github.com/takari/maven-wrapper).
and the [Maven wrapper](https://github.com/takari/maven-wrapper). However, SpotBugs has [limitations](https://gitlab.com/gitlab-org/gitlab/-/issues/350801) when used against [Ant](https://ant.apache.org/)-based projects. We recommend using the Semgrep-based analyzer for Ant-based Java projects.
### Multi-project support
@ -551,8 +551,8 @@ Some analyzers can be customized with CI/CD variables.
| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` scans. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
| `KUBESEC_HELM_OPTIONS` | Kubesec | Additional arguments for the `helm` executable. |
| `COMPILE` | Gosec, SpotBugs | Set to `false` to disable project compilation and dependency fetching. [Introduced for `SpotBugs`](https://gitlab.com/gitlab-org/gitlab/-/issues/195252) analyzer in GitLab 13.1 and [`Gosec`](https://gitlab.com/gitlab-org/gitlab/-/issues/330678) analyzer in GitLab 14.0. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |

View File

@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Operational Container Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/368828) the starboard directive in GitLab 15.4. The starboard directive will be removed in GitLab 16.0.
To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md).
You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab.
@ -19,12 +20,12 @@ to scan container images in your cluster for security vulnerabilities.
NOTE:
In GitLab 15.0 and later, you do not need to install Starboard operator in the Kubernetes cluster.
To begin scanning all resources in your cluster, add a `starboard`
To begin scanning all resources in your cluster, add a `container_scanning`
configuration block to your agent configuration with a `cadence` field
containing a CRON expression for when the scans will be run.
```yaml
starboard:
container_scanning:
cadence: '0 0 * * *' # Daily at 00:00 (Kubernetes cluster time)
```
@ -42,7 +43,7 @@ if you would like to scan only the `development`, `staging`, and `production`
namespaces, you can use this configuration:
```yaml
starboard:
container_scanning:
cadence: '0 0 * * *'
vulnerability_report:
namespaces:

View File

@ -208,9 +208,9 @@ from those IPs and allow them.
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
For outgoing connections from CI/CD runners, we are not providing static IP
addresses. All GitLab.com shared runners are deployed into Google Cloud Platform (GCP). Any
IP-based firewall can be configured by looking up all
For outgoing connections from CI/CD runners, we are not providing static IP addresses.
All GitLab.com shared runners are deployed into Google Cloud Platform (GCP) in `us-east1`.
Any IP-based firewall can be configured by looking up
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).
## Hostname list

View File

@ -1,6 +1,6 @@
---
stage: none
group: unassigned
stage: Manage
group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

View File

@ -70,6 +70,9 @@ The following items are exported:
- Merge request diffs
- Merge request comments
- Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Merge request multiple assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
- Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
- Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
- Labels
- Milestones
- Snippets
@ -92,7 +95,7 @@ Items that are **not** exported include:
- Pipeline triggers
- Webhooks
- Any encrypted tokens
- Merge Request Approvers and [the number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
- [Number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
- Repository size limits
- Deploy keys allowed to push to protected branches
- Secure Files

View File

@ -26976,6 +26976,9 @@ msgstr ""
msgid "Notify|%{invited_user} has %{highlight_start}declined%{highlight_end} your invitation to join the %{target_link} %{target_name}."
msgstr ""
msgid "Notify|%{issues} imported."
msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
@ -27015,6 +27018,12 @@ msgstr ""
msgid "Notify|Don't want to receive updates from GitLab administrators?"
msgstr ""
msgid "Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values."
msgstr ""
msgid "Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title."
msgstr ""
msgid "Notify|Fingerprint: %{fingerprint}"
msgstr ""
@ -27090,6 +27099,9 @@ msgstr ""
msgid "Notify|You have been mentioned in merge request %{mr_link}"
msgstr ""
msgid "Notify|Your CSV import for project %{project_link} has been completed."
msgstr ""
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@ -47044,6 +47056,11 @@ msgstr ""
msgid "limit of %{project_limit} reached"
msgstr ""
msgid "line"
msgid_plural "lines"
msgstr[0] ""
msgstr[1] ""
msgid "load it anyway"
msgstr ""

View File

@ -3,53 +3,54 @@
module RuboCop
module Cop
module Gitlab
class KeysFirstAndValuesFirst < RuboCop::Cop::Cop
FIRST_PATTERN = /\Afirst\z/.freeze
# Detects the use of `.keys.first` or `.values.first` and suggests a
# change to `.each_key.first` or `.each_value.first`. This reduces
# memory usage and execution time.
#
# @example
#
# # bad
#
# hash.keys.first
# hash.values.first
#
# # good
#
# hash.each_key.first
# hash.each_value.first
#
class KeysFirstAndValuesFirst < RuboCop::Cop::Base
extend RuboCop::Cop::AutoCorrector
def message(used_method)
<<~MSG
Don't use `.keys.first` and `.values.first`.
Instead use `.each_key.first` and `.each_value.first` (or `.first.first` and `first.second`)
MSG = 'Prefer `.%{autocorrect_method}.first` over `.%{current_method}.first`. ' \
'This reduces memory usage and execution time.'
This will reduce memory usage and execution time.
MSG
end
AUTOCORRECT_METHOD = {
keys: 'each_key',
values: 'each_value'
}.freeze
def_node_matcher :keys_or_values_first, <<~PATTERN
(send (send ({send const hash lvar} ...) ${:keys :values}) :first)
PATTERN
def on_send(node)
if find_on_keys_or_values?(node)
add_offense(node, location: :selector, message: message(node.method_name))
current_method = keys_or_values_first(node)
return unless current_method
autocorrect_method = AUTOCORRECT_METHOD.fetch(current_method)
msg = format(MSG, autocorrect_method: autocorrect_method, current_method: current_method)
add_offense(node.loc.selector, message: msg) do |corrector|
replacement = "#{autocorrect_expression(node)}.#{autocorrect_method}.first"
corrector.replace(node, replacement)
end
end
def autocorrect(node)
lambda do |corrector|
replace_with = if node.descendants.first.method_name == :values
'.each_value'
elsif node.descendants.first.method_name == :keys
'.each_key'
else
throw("Expect '.values.first' or '.keys.first', but get #{node.descendants.first.method_name}.first") # rubocop:disable Cop/BanCatchThrow
end
private
upto_including_keys_or_values = node.descendants.first.source_range
before_keys_or_values = node.descendants[1].source_range
range_to_replace = node.source_range
.with(begin_pos: before_keys_or_values.end_pos,
end_pos: upto_including_keys_or_values.end_pos)
corrector.replace(range_to_replace, replace_with)
end
end
def find_on_keys_or_values?(node)
chained_on_node = node.descendants.first
node.method_name.to_s =~ FIRST_PATTERN &&
chained_on_node.is_a?(RuboCop::AST::SendNode) &&
[:keys, :values].include?(chained_on_node.method_name) &&
node.descendants[1]
end
def method_name_for_node(node)
children[1].to_s
def autocorrect_expression(node)
node.receiver.receiver.source
end
end
end

View File

@ -331,7 +331,6 @@ RSpec.describe 'Group show page' do
end
it 'does not include structured markup in shared projects tab', :aggregate_failures, :js do
stub_feature_flags(group_overview_tabs_vue: false)
other_project = create(:project, :public)
other_project.project_group_links.create!(group: group)
@ -343,7 +342,6 @@ RSpec.describe 'Group show page' do
end
it 'does not include structured markup in archived projects tab', :aggregate_failures, :js do
stub_feature_flags(group_overview_tabs_vue: false)
project.update!(archived: true)
visit group_archived_path(group)

View File

@ -6,6 +6,7 @@ import OverviewTabs from '~/groups/components/overview_tabs.vue';
import GroupsApp from '~/groups/components/app.vue';
import GroupsStore from '~/groups/store/groups_store';
import GroupsService from '~/groups/service/groups_service';
import { createRouter } from '~/groups/init_overview_tabs';
import {
ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
ACTIVE_TAB_SHARED,
@ -13,6 +14,8 @@ import {
} from '~/groups/constants';
import axios from '~/lib/utils/axios_utils';
const router = createRouter();
describe('OverviewTabs', () => {
let wrapper;
@ -22,11 +25,19 @@ describe('OverviewTabs', () => {
archived: '/groups/foobar/-/children.json?archived=only',
};
const createComponent = async () => {
const routerMock = {
push: jest.fn(),
};
const createComponent = async ({
route = { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
} = {}) => {
wrapper = mountExtended(OverviewTabs, {
router,
provide: {
endpoints,
},
mocks: { $route: route, $router: routerMock },
});
await nextTick();
@ -34,6 +45,7 @@ describe('OverviewTabs', () => {
const findTabPanels = () => wrapper.findAllComponents(GlTab);
const findTab = (name) => wrapper.findByRole('tab', { name });
const findSelectedTab = () => wrapper.findByRole('tab', { selected: true });
afterEach(() => {
wrapper.destroy();
@ -42,15 +54,15 @@ describe('OverviewTabs', () => {
beforeEach(async () => {
// eslint-disable-next-line no-new
new AxiosMockAdapter(axios);
await createComponent();
});
it('renders `Subgroups and projects` tab with `GroupsApp` component', async () => {
await createComponent();
const tabPanel = findTabPanels().at(0);
expect(tabPanel.vm.$attrs).toMatchObject({
title: OverviewTabs.i18n.subgroupsAndProjects,
title: OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
lazy: false,
});
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
@ -63,14 +75,16 @@ describe('OverviewTabs', () => {
});
it('renders `Shared projects` tab and renders `GroupsApp` component after clicking tab', async () => {
await createComponent();
const tabPanel = findTabPanels().at(1);
expect(tabPanel.vm.$attrs).toMatchObject({
title: OverviewTabs.i18n.sharedProjects,
title: OverviewTabs.i18n[ACTIVE_TAB_SHARED],
lazy: true,
});
await findTab(OverviewTabs.i18n.sharedProjects).trigger('click');
await findTab(OverviewTabs.i18n[ACTIVE_TAB_SHARED]).trigger('click');
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
action: ACTIVE_TAB_SHARED,
@ -84,14 +98,16 @@ describe('OverviewTabs', () => {
});
it('renders `Archived projects` tab and renders `GroupsApp` component after clicking tab', async () => {
await createComponent();
const tabPanel = findTabPanels().at(2);
expect(tabPanel.vm.$attrs).toMatchObject({
title: OverviewTabs.i18n.archivedProjects,
title: OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
lazy: true,
});
await findTab(OverviewTabs.i18n.archivedProjects).trigger('click');
await findTab(OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED]).trigger('click');
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
action: ACTIVE_TAB_ARCHIVED,
@ -103,4 +119,69 @@ describe('OverviewTabs', () => {
expect(tabPanel.vm.$attrs.lazy).toBe(false);
});
describe.each([
[
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
{
name: ACTIVE_TAB_SHARED,
params: { group: ['foo', 'bar', 'baz'] },
},
],
[
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: ['foo', 'bar', 'baz'] } },
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
{
name: ACTIVE_TAB_SHARED,
params: { group: ['foo', 'bar', 'baz'] },
},
],
[
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo' } },
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
{
name: ACTIVE_TAB_SHARED,
params: { group: ['foo'] },
},
],
[
{ name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
{
name: ACTIVE_TAB_ARCHIVED,
params: { group: ['foo', 'bar'] },
},
],
[
{ name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
{
name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
params: { group: ['foo', 'bar'] },
},
],
[
{ name: ACTIVE_TAB_ARCHIVED, params: { group: ['foo'] } },
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
{
name: ACTIVE_TAB_SHARED,
params: { group: ['foo'] },
},
],
])('when current route is %j', (currentRoute, tabToClick, expectedRoute) => {
beforeEach(async () => {
await createComponent({ route: currentRoute });
});
it(`sets ${OverviewTabs.i18n[currentRoute.name]} as active tab`, () => {
expect(findSelectedTab().text()).toBe(OverviewTabs.i18n[currentRoute.name]);
});
it(`pushes expected route when ${tabToClick} tab is clicked`, async () => {
await findTab(tabToClick).trigger('click');
expect(routerMock.push).toHaveBeenCalledWith(expectedRoute);
});
});
});

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::Environments::LastDeploymentResolver do
include GraphqlHelpers
include Gitlab::Graphql::Laziness
let_it_be(:project) { create(:project, :repository, :private) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:deployment) { create(:deployment, :created, environment: environment, project: project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let(:current_user) { developer }
describe '#resolve' do
it 'finds the deployment when status matches' do
expect(resolve_last_deployment(status: :created)).to eq(deployment)
end
it 'does not find the deployment when status does not match' do
expect(resolve_last_deployment(status: :success)).to be_nil
end
it 'raises an error when status is not specified' do
expect { resolve_last_deployment }.to raise_error(ArgumentError)
end
it 'raises an error when status is not supported' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError,
'"skipped" status is not supported.') do
resolve_last_deployment(status: :skipped)
end
end
end
def resolve_last_deployment(args = {}, context = { current_user: current_user })
force(resolve(described_class, obj: environment, ctx: context, args: args))
end
end

View File

@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Environment'] do
it 'has the expected fields' do
expected_fields = %w[
name id state metrics_dashboard latest_opened_most_severe_alert path external_url deployments
slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType
slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment
]
expect(described_class).to have_graphql_fields(*expected_fields)

View File

@ -2,11 +2,9 @@
require "spec_helper"
RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
let(:rugged) do
Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
end
RSpec.describe Gitlab::Git::Blob do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:repository) { project.repository.raw }
describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
@ -44,7 +42,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
shared_examples '.find' do
context 'nil path' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], nil) }
it { expect(blob).to eq(nil) }
end
@ -56,30 +54,30 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'blank path' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], '') }
it { expect(blob).to eq(nil) }
end
context 'file in subdir' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb") }
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
end
context 'file in root' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, ".gitignore") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], ".gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
@ -87,25 +85,25 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file in root with leading slash' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "/.gitignore") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "/.gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
end
context 'non-exist file' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "missing.rb") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "missing.rb") }
it { expect(blob).to be_nil }
end
context 'six submodule' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'six') }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'six') }
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
it { expect(blob.data).to eq('') }
@ -121,7 +119,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'large file' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg') }
let(:blob_size) { 111803 }
let(:stub_limit) { 1000 }
@ -159,10 +157,10 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.find with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
allow_next_instance_of(Rugged) do |instance|
allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
allow(instance).to receive(:rev_parse).with(TestEnv::BRANCH_SHA['master']).and_call_original
end
described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
described_class.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg')
end
it_behaves_like '.find'
@ -177,40 +175,13 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(raw_blob.size).to eq(669) }
it { expect(raw_blob.truncated?).to be_falsey }
it { expect(bad_blob).to be_nil }
context 'large file' do
it 'limits the size of a large file' do
blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
buffer = Array.new(blob_size, 0)
rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
expect(blob.size).to eq(blob_size)
expect(blob.loaded_size).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
expect(blob.data.length).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
expect(blob.truncated?).to be_truthy
blob.load_all_data!(repository)
expect(blob.loaded_size).to eq(blob_size)
end
end
context 'when sha references a tree' do
it 'returns nil' do
tree = rugged.rev_parse('master^{tree}')
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
expect(blob).to be_nil
end
end
end
describe '.batch' do
let(:blob_references) do
[
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
[SeedRepo::Commit::ID, 'six']
[TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
[TestEnv::BRANCH_SHA['master'], 'six']
]
end
@ -224,7 +195,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
@ -273,21 +244,21 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
context 'when large number of blobs requested' do
let(:first_batch) do
[
[SeedRepo::Commit::ID, 'files/ruby/popen.rb'],
[SeedRepo::Commit::ID, 'six']
[TestEnv::BRANCH_SHA['master'], 'files/ruby/popen.rb'],
[TestEnv::BRANCH_SHA['master'], 'six']
]
end
let(:second_batch) do
[
[SeedRepo::Commit::ID, 'some'],
[SeedRepo::Commit::ID, 'other']
[TestEnv::BRANCH_SHA['master'], 'some'],
[TestEnv::BRANCH_SHA['master'], 'other']
]
end
let(:third_batch) do
[
[SeedRepo::Commit::ID, 'files']
[TestEnv::BRANCH_SHA['master'], 'files']
]
end
@ -315,8 +286,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.batch_metadata' do
let(:blob_references) do
[
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
[SeedRepo::Commit::ID, 'six']
[TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
[TestEnv::BRANCH_SHA['master'], 'six']
]
end
@ -333,8 +304,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
describe '.batch_lfs_pointers' do
let(:tree_object) { rugged.rev_parse('master^{tree}') }
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
repository,
@ -346,8 +315,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:lfs_blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/image.jpg'
TestEnv::BRANCH_SHA['master'],
'files/lfs/lfs_object.iso'
)
end
@ -374,12 +343,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
expect(blobs_2).to eq([])
end
it 'silently ignores tree objects' do
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
expect(blobs).to eq([])
end
it 'silently ignores non lfs objects' do
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
@ -398,7 +361,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe 'encoding', :aggregate_failures do
context 'file with russian text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/russian.rb") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("russian.rb")
@ -412,7 +375,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with Japanese text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/テスト.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("テスト.txt")
@ -424,12 +387,12 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with ISO-8859 text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::LastCommit::ID, "encoding/iso8859.txt") }
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/iso8859.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("iso8859.txt")
expect(blob.loaded_size).to eq(4)
expect(blob.size).to eq(4)
expect(blob.loaded_size).to eq(3)
expect(blob.size).to eq(3)
expect(blob.mode).to eq("100644")
expect(blob.truncated?).to be_falsey
end
@ -441,7 +404,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
TestEnv::BRANCH_SHA['master'],
'files/ruby/regex.rb'
)
end
@ -456,14 +419,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
TestEnv::BRANCH_SHA['with-executables'],
'files/executables/ls'
)
end
it { expect(blob.name).to eq('ls') }
it { expect(blob.path).to eq('files/executables/ls') }
it { expect(blob.size).to eq(110080) }
it { expect(blob.size).to eq(23) }
it { expect(blob.mode).to eq("100755") }
end
@ -471,29 +434,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
'files/links/ruby-style-guide.md'
'88ce9520c07b7067f589b7f83a30b6250883115c',
'symlink'
)
end
it { expect(blob.name).to eq('ruby-style-guide.md') }
it { expect(blob.path).to eq('files/links/ruby-style-guide.md') }
it { expect(blob.size).to eq(31) }
it { expect(blob.mode).to eq("120000") }
end
context 'file symlink to binary' do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
'files/links/touch'
)
end
it { expect(blob.name).to eq('touch') }
it { expect(blob.path).to eq('files/links/touch') }
it { expect(blob.size).to eq(20) }
it { expect(blob.name).to eq('symlink') }
it { expect(blob.path).to eq('symlink') }
it { expect(blob.size).to eq(6) }
it { expect(blob.mode).to eq("120000") }
end
end
@ -503,79 +451,20 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/image.jpg'
TestEnv::BRANCH_SHA['png-lfs'],
'files/images/emoji.png'
)
end
it { expect(blob.lfs_pointer?).to eq(true) }
it { expect(blob.lfs_oid).to eq("4206f951d2691c78aac4c0ce9f2b23580b2c92cdcc4336e1028742c0274938e0") }
it { expect(blob.lfs_size).to eq(19548) }
it { expect(blob.id).to eq("f4d76af13003d1106be7ac8c5a2a3d37ddf32c2a") }
it { expect(blob.name).to eq("image.jpg") }
it { expect(blob.path).to eq("files/lfs/image.jpg") }
it { expect(blob.size).to eq(130) }
it { expect(blob.lfs_oid).to eq("96f74c6fe7a2979eefb9ec74a5dfc6888fb25543cf99b77586b79afea1da6f97") }
it { expect(blob.lfs_size).to eq(1219696) }
it { expect(blob.id).to eq("ff0ab3afd1616ff78d0331865d922df103b64cf0") }
it { expect(blob.name).to eq("emoji.png") }
it { expect(blob.path).to eq("files/images/emoji.png") }
it { expect(blob.size).to eq(132) }
it { expect(blob.mode).to eq("100644") }
end
describe 'file an invalid lfs pointer' do
context 'with correct version header but incorrect size and oid' do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/archive-invalid.tar'
)
end
it { expect(blob.lfs_pointer?).to eq(false) }
it { expect(blob.lfs_oid).to eq(nil) }
it { expect(blob.lfs_size).to eq(nil) }
it { expect(blob.id).to eq("f8a898db217a5a85ed8b3d25b34c1df1d1094c46") }
it { expect(blob.name).to eq("archive-invalid.tar") }
it { expect(blob.path).to eq("files/lfs/archive-invalid.tar") }
it { expect(blob.size).to eq(43) }
it { expect(blob.mode).to eq("100644") }
end
context 'with correct version header and size but incorrect size and oid' do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/picture-invalid.png'
)
end
it { expect(blob.lfs_pointer?).to eq(false) }
it { expect(blob.lfs_oid).to eq(nil) }
it { expect(blob.lfs_size).to eq(1575078) }
it { expect(blob.id).to eq("5ae35296e1f95c1ef9feda1241477ed29a448572") }
it { expect(blob.name).to eq("picture-invalid.png") }
it { expect(blob.path).to eq("files/lfs/picture-invalid.png") }
it { expect(blob.size).to eq(57) }
it { expect(blob.mode).to eq("100644") }
end
context 'with correct version header and size but invalid size and oid' do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/file-invalid.zip'
)
end
it { expect(blob.lfs_pointer?).to eq(false) }
it { expect(blob.lfs_oid).to eq(nil) }
it { expect(blob.lfs_size).to eq(nil) }
it { expect(blob.id).to eq("d831981bd876732b85a1bcc6cc01210c9f36248f") }
it { expect(blob.name).to eq("file-invalid.zip") }
it { expect(blob.path).to eq("files/lfs/file-invalid.zip") }
it { expect(blob.size).to eq(60) }
it { expect(blob.mode).to eq("100644") }
end
end
end
describe '#load_all_data!' do

View File

@ -563,6 +563,16 @@ RSpec.describe Deployment do
end
end
describe '.ordered_as_upcoming' do
let!(:deployment1) { create(:deployment, status: :running) }
let!(:deployment2) { create(:deployment, status: :blocked) }
let!(:deployment3) { create(:deployment, status: :created) }
it 'sorts by ID DESC' do
expect(described_class.ordered_as_upcoming).to eq([deployment3, deployment2, deployment1])
end
end
describe 'visible' do
subject { described_class.visible }

View File

@ -948,6 +948,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe 'Last deployment relations' do
Deployment::FINISHED_STATUSES.each do |status|
it "returns the last #{status} deployment" do
create(:deployment, status.to_sym, environment: environment, finished_at: 1.day.ago)
expected = create(:deployment, status.to_sym, environment: environment, finished_at: Time.current)
expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
end
end
Deployment::UPCOMING_STATUSES.each do |status|
it "returns the last #{status} deployment" do
create(:deployment, status.to_sym, environment: environment)
expected = create(:deployment, status.to_sym, environment: environment)
expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
end
end
end
describe '#last_deployable' do
subject { environment.last_deployable }

View File

@ -6,7 +6,8 @@ RSpec.describe 'Project Environments query' do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be_with_refind(:production) { create(:environment, :production, project: project) }
let_it_be_with_refind(:staging) { create(:environment, :staging, project: project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
subject { post_graphql(query, current_user: user) }
@ -17,7 +18,7 @@ RSpec.describe 'Project Environments query' do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
environment(name: "#{production.name}") {
slug
createdAt
updatedAt
@ -32,17 +33,101 @@ RSpec.describe 'Project Environments query' do
end
it 'returns the specified fields of the environment', :aggregate_failures do
environment.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review')
production.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review')
subject
environment_data = graphql_data.dig('project', 'environment')
expect(environment_data['slug']).to eq(environment.slug)
expect(environment_data['createdAt']).to eq(environment.created_at.iso8601)
expect(environment_data['updatedAt']).to eq(environment.updated_at.iso8601)
expect(environment_data['autoStopAt']).to eq(environment.auto_stop_at.iso8601)
expect(environment_data['autoDeleteAt']).to eq(environment.auto_delete_at.iso8601)
expect(environment_data['tier']).to eq(environment.tier.upcase)
expect(environment_data['environmentType']).to eq(environment.environment_type)
expect(environment_data['slug']).to eq(production.slug)
expect(environment_data['createdAt']).to eq(production.created_at.iso8601)
expect(environment_data['updatedAt']).to eq(production.updated_at.iso8601)
expect(environment_data['autoStopAt']).to eq(production.auto_stop_at.iso8601)
expect(environment_data['autoDeleteAt']).to eq(production.auto_delete_at.iso8601)
expect(environment_data['tier']).to eq(production.tier.upcase)
expect(environment_data['environmentType']).to eq(production.environment_type)
end
describe 'last deployments of environments' do
::Deployment.statuses.each do |status, _|
let_it_be(:"production_#{status}_deployment") do
create(:deployment, status.to_sym, environment: production, project: project)
end
let_it_be(:"staging_#{status}_deployment") do
create(:deployment, status.to_sym, environment: staging, project: project)
end
end
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environments {
nodes {
name
lastSuccessDeployment: lastDeployment(status: SUCCESS) {
iid
}
lastRunningDeployment: lastDeployment(status: RUNNING) {
iid
}
lastBlockedDeployment: lastDeployment(status: BLOCKED) {
iid
}
}
}
}
}
)
end
it 'returns all last deployments of the environment' do
subject
environments_data = graphql_data_at(:project, :environments, :nodes)
environments_data.each do |environment_data|
name = environment_data['name']
success_deployment = public_send(:"#{name}_success_deployment")
running_deployment = public_send(:"#{name}_running_deployment")
blocked_deployment = public_send(:"#{name}_blocked_deployment")
expect(environment_data['lastSuccessDeployment']['iid']).to eq(success_deployment.iid.to_s)
expect(environment_data['lastRunningDeployment']['iid']).to eq(running_deployment.iid.to_s)
expect(environment_data['lastBlockedDeployment']['iid']).to eq(blocked_deployment.iid.to_s)
end
end
it 'executes the same number of queries in single environment and multiple environments' do
single_environment_query =
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{production.name}") {
name
lastSuccessDeployment: lastDeployment(status: SUCCESS) {
iid
}
lastRunningDeployment: lastDeployment(status: RUNNING) {
iid
}
lastBlockedDeployment: lastDeployment(status: BLOCKED) {
iid
}
}
}
}
)
baseline = ActiveRecord::QueryRecorder.new do
run_with_clean_state(single_environment_query, context: { current_user: user })
end
multi = ActiveRecord::QueryRecorder.new do
run_with_clean_state(query, context: { current_user: user })
end
expect(multi).not_to exceed_query_limit(baseline)
end
end
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/keys_first_and_values_first'
RSpec.describe RuboCop::Cop::Gitlab::KeysFirstAndValuesFirst do
let(:msg) { described_class::MSG }
subject(:cop) { described_class.new }
shared_examples 'inspect use of keys or values first' do |method, autocorrect|
describe ".#{method}.first" do
it 'flags and autocorrects' do
expect_offense(<<~RUBY, method: method, autocorrect: autocorrect)
hash.%{method}.first
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
var = {a: 1}; var.%{method}.first
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
{a: 1}.%{method}.first
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
CONST.%{method}.first
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
::CONST.%{method}.first
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
RUBY
expect_correction(<<~RUBY)
hash.#{autocorrect}.first
var = {a: 1}; var.#{autocorrect}.first
{a: 1}.#{autocorrect}.first
CONST.#{autocorrect}.first
::CONST.#{autocorrect}.first
RUBY
end
it 'does not flag unrelated code' do
expect_no_offenses(<<~RUBY)
array.first
hash.#{method}.last
hash.#{method}
#{method}.first
1.#{method}.first
'string'.#{method}.first
RUBY
end
end
end
it_behaves_like 'inspect use of keys or values first', :keys, :each_key
it_behaves_like 'inspect use of keys or values first', :values, :each_value
end

View File

@ -88,7 +88,8 @@ module TestEnv
'two-commits' => '304d257',
'utf-16' => 'f05a987',
'gitaly-rename-test' => '94bb47c',
'smime-signed-commits' => 'ed775cc'
'smime-signed-commits' => 'ed775cc',
'Ääh-test-utf-8' => '7975be0'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'notify/import_issues_csv_email.html.haml' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:correct_results) { { success: 3, valid_file: true } }
let(:errored_results) { { success: 3, error_lines: [5, 6, 7], valid_file: true } }
let(:parse_error_results) { { success: 0, parse_error: true } }
before do
assign(:user, user)
assign(:project, project)
end
context 'when no errors found while importing' do
before do
assign(:results, correct_results)
end
it 'renders correctly' do
render
expect(rendered).to have_link(project.full_name, href: project_url(project))
expect(rendered).to have_content("3 issues imported")
expect(rendered).not_to have_content("Errors found on line")
expect(rendered).not_to have_content(
"Error parsing CSV file. Please make sure it has the correct format: \
a delimited text file that uses a comma to separate values.")
end
end
context 'when import errors reported' do
before do
assign(:results, errored_results)
end
it 'renders correctly' do
render
expect(rendered).to have_content("Errors found on lines: #{errored_results[:error_lines].join(", ")}. \
Please check if these lines have an issue title.")
expect(rendered).not_to have_content("Error parsing CSV file. Please make sure it has the correct format: \
a delimited text file that uses a comma to separate values.")
end
end
context 'when parse error reported while importing' do
before do
assign(:results, parse_error_results)
end
it 'renders with parse error' do
render
expect(rendered).to have_content("Error parsing CSV file. \
Please make sure it has the correct format: a delimited text file that uses a comma to separate values.")
end
end
end