Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									830a1f59e2
								
							
						
					
					
						commit
						75d101a1c2
					
				|  | @ -1 +1 @@ | |||
| 684af7592d733edf7a16a817cd7900f03755cfb7 | ||||
| d47724f6e9e18fd7c7c73ec68d89ed874c841502 | ||||
|  |  | |||
|  | @ -2,8 +2,9 @@ | |||
| import { GlButton, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui'; | ||||
| import { helpPagePath } from '~/helpers/help_page_helper'; | ||||
| import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; | ||||
| import NewIssueDropdown from '~/vue_shared/components/new_issue_dropdown/new_issue_dropdown.vue'; | ||||
| import { i18n } from '../constants'; | ||||
| import NewIssueDropdown from './new_issue_dropdown.vue'; | ||||
| import { hasNewIssueDropdown } from '../has_new_issue_dropdown_mixin'; | ||||
| 
 | ||||
| export default { | ||||
|   i18n, | ||||
|  | @ -16,6 +17,7 @@ export default { | |||
|     GlSprintf, | ||||
|     NewIssueDropdown, | ||||
|   }, | ||||
|   mixins: [hasNewIssueDropdown()], | ||||
|   inject: [ | ||||
|     'canCreateProjects', | ||||
|     'emptyStateSvgPath', | ||||
|  | @ -75,7 +77,13 @@ export default { | |||
|           :export-csv-path="exportCsvPathWithQuery" | ||||
|           :issuable-count="currentTabCount" | ||||
|         /> | ||||
|         <new-issue-dropdown v-if="showNewIssueDropdown" class="gl-align-self-center" /> | ||||
|         <new-issue-dropdown | ||||
|           v-if="showNewIssueDropdown" | ||||
|           class="gl-align-self-center" | ||||
|           :query="$options.searchProjectsQuery" | ||||
|           :query-variables="newIssueDropdownQueryVariables" | ||||
|           :extract-projects="extractProjects" | ||||
|         /> | ||||
|       </template> | ||||
|     </gl-empty-state> | ||||
|     <hr /> | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ import { | |||
| import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; | ||||
| import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants'; | ||||
| import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import NewIssueDropdown from '~/vue_shared/components/new_issue_dropdown/new_issue_dropdown.vue'; | ||||
| import { | ||||
|   CREATED_DESC, | ||||
|   defaultTypeTokenOptions, | ||||
|  | @ -82,9 +83,9 @@ import { | |||
|   getSortOptions, | ||||
|   isSortKey, | ||||
| } from '../utils'; | ||||
| import { hasNewIssueDropdown } from '../has_new_issue_dropdown_mixin'; | ||||
| import EmptyStateWithAnyIssues from './empty_state_with_any_issues.vue'; | ||||
| import EmptyStateWithoutAnyIssues from './empty_state_without_any_issues.vue'; | ||||
| import NewIssueDropdown from './new_issue_dropdown.vue'; | ||||
| 
 | ||||
| const UserToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/user_token.vue'); | ||||
| const EmojiToken = () => | ||||
|  | @ -117,7 +118,7 @@ export default { | |||
|   directives: { | ||||
|     GlTooltip: GlTooltipDirective, | ||||
|   }, | ||||
|   mixins: [glFeatureFlagMixin()], | ||||
|   mixins: [glFeatureFlagMixin(), hasNewIssueDropdown()], | ||||
|   inject: [ | ||||
|     'autocompleteAwardEmojisPath', | ||||
|     'calendarPath', | ||||
|  | @ -831,7 +832,12 @@ export default { | |||
|           {{ $options.i18n.newIssueLabel }} | ||||
|         </gl-button> | ||||
|         <slot name="new-objective-button"></slot> | ||||
|         <new-issue-dropdown v-if="showNewIssueDropdown" /> | ||||
|         <new-issue-dropdown | ||||
|           v-if="showNewIssueDropdown" | ||||
|           :query="$options.searchProjectsQuery" | ||||
|           :query-variables="newIssueDropdownQueryVariables" | ||||
|           :extract-projects="extractProjects" | ||||
|         /> | ||||
|       </template> | ||||
| 
 | ||||
|       <template #timeframe="{ issuable = {} }"> | ||||
|  |  | |||
|  | @ -0,0 +1,18 @@ | |||
| import searchProjectsQuery from './queries/search_projects.query.graphql'; | ||||
| 
 | ||||
| export const hasNewIssueDropdown = () => ({ | ||||
|   inject: ['fullPath'], | ||||
|   computed: { | ||||
|     newIssueDropdownQueryVariables() { | ||||
|       return { | ||||
|         fullPath: this.fullPath, | ||||
|       }; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     extractProjects(data) { | ||||
|       return data?.group?.projects?.nodes; | ||||
|     }, | ||||
|   }, | ||||
|   searchProjectsQuery, | ||||
| }); | ||||
|  | @ -9,16 +9,17 @@ export const HTTP_STATUS_PARTIAL_CONTENT = 206; | |||
| export const HTTP_STATUS_MULTI_STATUS = 207; | ||||
| export const HTTP_STATUS_ALREADY_REPORTED = 208; | ||||
| export const HTTP_STATUS_IM_USED = 226; | ||||
| export const HTTP_STATUS_METHOD_NOT_ALLOWED = 405; | ||||
| export const HTTP_STATUS_CONFLICT = 409; | ||||
| export const HTTP_STATUS_GONE = 410; | ||||
| export const HTTP_STATUS_PAYLOAD_TOO_LARGE = 413; | ||||
| export const HTTP_STATUS_UNPROCESSABLE_ENTITY = 422; | ||||
| export const HTTP_STATUS_TOO_MANY_REQUESTS = 429; | ||||
| export const HTTP_STATUS_BAD_REQUEST = 400; | ||||
| export const HTTP_STATUS_UNAUTHORIZED = 401; | ||||
| export const HTTP_STATUS_FORBIDDEN = 403; | ||||
| export const HTTP_STATUS_NOT_FOUND = 404; | ||||
| export const HTTP_STATUS_METHOD_NOT_ALLOWED = 405; | ||||
| export const HTTP_STATUS_CONFLICT = 409; | ||||
| export const HTTP_STATUS_GONE = 410; | ||||
| export const HTTP_STATUS_PAYLOAD_TOO_LARGE = 413; | ||||
| export const HTTP_STATUS_IM_A_TEAPOT = 418; | ||||
| export const HTTP_STATUS_UNPROCESSABLE_ENTITY = 422; | ||||
| export const HTTP_STATUS_TOO_MANY_REQUESTS = 429; | ||||
| export const HTTP_STATUS_INTERNAL_SERVER_ERROR = 500; | ||||
| export const HTTP_STATUS_SERVICE_UNAVAILABLE = 503; | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,11 @@ | |||
| query searchUserProjects($search: String) { | ||||
|   projects(search: $search, membership: true, sort: "latest_activity_desc") { | ||||
|     nodes { | ||||
|       id | ||||
|       issuesEnabled | ||||
|       name | ||||
|       nameWithNamespace | ||||
|       webUrl | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -10,7 +10,7 @@ import { createAlert } from '~/flash'; | |||
| import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; | ||||
| import { __, sprintf } from '~/locale'; | ||||
| import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; | ||||
| import searchProjectsQuery from '../queries/search_projects.query.graphql'; | ||||
| import searchUserProjects from './graphql/search_user_projects.query.graphql'; | ||||
| 
 | ||||
| export default { | ||||
|   i18n: { | ||||
|  | @ -25,7 +25,23 @@ export default { | |||
|     GlLoadingIcon, | ||||
|     GlSearchBoxByType, | ||||
|   }, | ||||
|   inject: ['fullPath'], | ||||
|   props: { | ||||
|     query: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => searchUserProjects, | ||||
|     }, | ||||
|     queryVariables: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     extractProjects: { | ||||
|       type: Function, | ||||
|       required: false, | ||||
|       default: (data) => data?.projects?.nodes, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       projects: [], | ||||
|  | @ -36,14 +52,18 @@ export default { | |||
|   }, | ||||
|   apollo: { | ||||
|     projects: { | ||||
|       query: searchProjectsQuery, | ||||
|       query() { | ||||
|         return this.query; | ||||
|       }, | ||||
|       variables() { | ||||
|         return { | ||||
|           fullPath: this.fullPath, | ||||
|           search: this.search, | ||||
|           ...this.queryVariables, | ||||
|         }; | ||||
|       }, | ||||
|       update: ({ group }) => group.projects.nodes ?? [], | ||||
|       update(data) { | ||||
|         return this.extractProjects(data) || []; | ||||
|       }, | ||||
|       error(error) { | ||||
|         createAlert({ | ||||
|           message: __('An error occurred while loading projects.'), | ||||
|  | @ -1,26 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module ZuoraCSP | ||||
|   extend ActiveSupport::Concern | ||||
| 
 | ||||
|   ZUORA_URL = 'https://*.zuora.com' | ||||
| 
 | ||||
|   included do | ||||
|     content_security_policy do |policy| | ||||
|       next if policy.directives.blank? | ||||
| 
 | ||||
|       default_script_src = policy.directives['script-src'] || policy.directives['default-src'] | ||||
|       script_src_values = Array.wrap(default_script_src) | ["'self'", "'unsafe-eval'", ZUORA_URL] | ||||
| 
 | ||||
|       default_frame_src = policy.directives['frame-src'] || policy.directives['default-src'] | ||||
|       frame_src_values = Array.wrap(default_frame_src) | ["'self'", ZUORA_URL] | ||||
| 
 | ||||
|       default_child_src = policy.directives['child-src'] || policy.directives['default-src'] | ||||
|       child_src_values = Array.wrap(default_child_src) | ["'self'", ZUORA_URL] | ||||
| 
 | ||||
|       policy.script_src(*script_src_values) | ||||
|       policy.frame_src(*frame_src_values) | ||||
|       policy.child_src(*child_src_values) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -5,7 +5,6 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|   include RedisTracking | ||||
|   include ProductAnalyticsTracking | ||||
|   include ProjectStatsRefreshConflictsGuard | ||||
|   include ZuoraCSP | ||||
| 
 | ||||
|   urgency :low, [ | ||||
|     :index, :new, :builds, :show, :failures, :create, | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ module Projects | |||
|   module Settings | ||||
|     class CiCdController < Projects::ApplicationController | ||||
|       include RunnerSetupScripts | ||||
|       include ZuoraCSP | ||||
| 
 | ||||
|       NUMBER_OF_RUNNERS_PER_PAGE = 20 | ||||
| 
 | ||||
|  |  | |||
|  | @ -394,6 +394,7 @@ class MergeRequest < ApplicationRecord | |||
|   scope :order_closed_at_desc, -> { order_by_metric(:latest_closed_at, 'DESC') } | ||||
|   scope :preload_source_project, -> { preload(:source_project) } | ||||
|   scope :preload_target_project, -> { preload(:target_project) } | ||||
|   scope :preload_target_project_with_namespace, -> { preload(target_project: [:namespace]) } | ||||
|   scope :preload_routables, -> do | ||||
|     preload(target_project: [:route, { namespace: :route }], | ||||
|             source_project: [:route, { namespace: :route }]) | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddNotNullConstraintToOAuthAccessTokensExpiresIn < Gitlab::Database::Migration[2.1] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     # validate: false ensures that existing records are not affected | ||||
|     # https://docs.gitlab.com/ee/development/database/not_null_constraints.html#prevent-new-invalid-records-current-release | ||||
|     add_not_null_constraint :oauth_access_tokens, :expires_in, validate: false | ||||
|     change_column_default :oauth_access_tokens, :expires_in, 7200 | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_not_null_constraint :oauth_access_tokens, :expires_in | ||||
|     change_column_default :oauth_access_tokens, :expires_in, nil | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 16e7446f8fba7fe0b76559432ac6ecc30261a5775b9f914c77425ceab3b92315 | ||||
|  | @ -18479,7 +18479,7 @@ CREATE TABLE oauth_access_tokens ( | |||
|     application_id integer, | ||||
|     token character varying NOT NULL, | ||||
|     refresh_token character varying, | ||||
|     expires_in integer, | ||||
|     expires_in integer DEFAULT 7200, | ||||
|     revoked_at timestamp without time zone, | ||||
|     created_at timestamp without time zone NOT NULL, | ||||
|     scopes character varying | ||||
|  | @ -25632,6 +25632,9 @@ ALTER TABLE ONLY chat_teams | |||
| ALTER TABLE vulnerability_scanners | ||||
|     ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID; | ||||
| 
 | ||||
| ALTER TABLE oauth_access_tokens | ||||
|     ADD CONSTRAINT check_70f294ef54 CHECK ((expires_in IS NOT NULL)) NOT VALID; | ||||
| 
 | ||||
| ALTER TABLE sprints | ||||
|     ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID; | ||||
| 
 | ||||
|  |  | |||
|  | @ -397,14 +397,15 @@ Example response: | |||
| Download a release asset file by making a request with the following format: | ||||
| 
 | ||||
| ```plaintext | ||||
| GET /projects/:id/releases/:tag_name/downloads/:filepath | ||||
| GET /projects/:id/releases/:tag_name/downloads/:direct_asset_path | ||||
| ``` | ||||
| 
 | ||||
| | Attribute                  | Type           | Required | Description                                                                         | | ||||
| |----------------------------| -------------- | -------- | ----------------------------------------------------------------------------------- | | ||||
| | `id`                       | integer/string | yes      | The ID or [URL-encoded path of the project](../rest/index.md#namespaced-path-encoding).  | | ||||
| | `tag_name`                 | string         | yes      | The Git tag the release is associated with.                                         | | ||||
| | `filepath`                 | string         | yes      | Path to the release asset file as specified when [creating](links.md#create-a-release-link) or [updating](links.md#update-a-release-link) its link. | | ||||
| | `filepath`                 | string         | yes      | Deprecated: Use `direct_asset_path` instead.                                        | | ||||
| | `direct_asset_path`        | string         | yes      | Path to the release asset file as specified when [creating](links.md#create-a-release-link) or [updating](links.md#update-a-release-link) its link. | | ||||
| 
 | ||||
| Example request: | ||||
| 
 | ||||
|  | @ -463,15 +464,16 @@ POST /projects/:id/releases | |||
| | `assets:links`     | array of hash   | no                          | An array of assets links.                                                                                                        | | ||||
| | `assets:links:name`| string          | required by: `assets:links` | The name of the link. Link names must be unique within the release.                                                              | | ||||
| | `assets:links:url` | string          | required by: `assets:links` | The URL of the link. Link URLs must be unique within the release.                                                                | | ||||
| | `assets:links:filepath` | string     | no | Optional path for a [Direct Asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | ||||
| | `assets:links:link_type` | string     | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. | ||||
| | `assets:links:filepath` | string     | no | Deprecated: Use `direct_asset_path` instead. | | ||||
| | `assets:links:direct_asset_path` | string     | no | Optional path for a [direct asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | | ||||
| | `assets:links:link_type` | string     | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. | | ||||
| | `released_at`      | datetime        | no                          | Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). Only provide this field if creating an [upcoming](../../user/project/releases/index.md#upcoming-releases) or [historical](../../user/project/releases/index.md#historical-releases) release.  | | ||||
| 
 | ||||
| Example request: | ||||
| 
 | ||||
| ```shell | ||||
| curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: <your_access_token>" \ | ||||
|      --data '{ "name": "New release", "tag_name": "v0.3", "description": "Super nice release", "milestones": ["v1.0", "v1.0-rc"], "assets": { "links": [{ "name": "hoge", "url": "https://google.com", "filepath": "/binaries/linux-amd64", "link_type":"other" }] } }' \ | ||||
|      --data '{ "name": "New release", "tag_name": "v0.3", "description": "Super nice release", "milestones": ["v1.0", "v1.0-rc"], "assets": { "links": [{ "name": "hoge", "url": "https://google.com", "direct_asset_path": "/binaries/linux-amd64", "link_type":"other" }] } }' \ | ||||
|      --request POST "https://gitlab.example.com/api/v4/projects/24/releases" | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
|  | @ -93,14 +93,15 @@ Creates an asset as a link from a release. | |||
| POST /projects/:id/releases/:tag_name/assets/links | ||||
| ``` | ||||
| 
 | ||||
| | Attribute   | Type           | Required | Description                                                                                                               | | ||||
| |-------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------| | ||||
| | `id`        | integer/string | yes      | The ID or [URL-encoded path of the project](../rest/index.md#namespaced-path-encoding).                                        | | ||||
| | `tag_name`  | string         | yes      | The tag associated with the Release.                                                                                      | | ||||
| | `name`      | string         | yes      | The name of the link. Link names must be unique in the release.                                                           | | ||||
| | `url`       | string         | yes      | The URL of the link. Link URLs must be unique in the release.                                                             | | ||||
| | `filepath`  | string         | no       | Optional path for a [Direct Asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | | ||||
| | `link_type` | string         | no       | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`.                                        | | ||||
| | Attribute            | Type           | Required | Description                                                                                                               | | ||||
| |----------------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------| | ||||
| | `id`                 | integer/string | yes      | The ID or [URL-encoded path of the project](../rest/index.md#namespaced-path-encoding).                                        | | ||||
| | `tag_name`           | string         | yes      | The tag associated with the Release.                                                                                      | | ||||
| | `name`               | string         | yes      | The name of the link. Link names must be unique in the release.                                                           | | ||||
| | `url`                | string         | yes      | The URL of the link. Link URLs must be unique in the release.                                                             | | ||||
| | `filepath`           | string         | no       | Deprecated: Use `direct_asset_path` instead.                                                                              | | ||||
| | `direct_asset_path`  | string         | no       | Optional path for a [direct asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | | ||||
| | `link_type`          | string         | no       | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`.                                        | | ||||
| 
 | ||||
| Example request: | ||||
| 
 | ||||
|  | @ -109,7 +110,7 @@ curl --request POST \ | |||
|     --header "PRIVATE-TOKEN: <your_access_token>" \ | ||||
|     --data name="hellodarwin-amd64" \ | ||||
|     --data url="https://gitlab.example.com/mynamespace/hello/-/jobs/688/artifacts/raw/bin/hello-darwin-amd64" \ | ||||
|     --data filepath="/bin/hellodarwin-amd64" \ | ||||
|     --data direct_asset_path="/bin/hellodarwin-amd64" \ | ||||
|     "https://gitlab.example.com/api/v4/projects/20/releases/v1.7.0/assets/links" | ||||
| ``` | ||||
| 
 | ||||
|  | @ -134,15 +135,16 @@ Updates an asset as a link from a release. | |||
| PUT /projects/:id/releases/:tag_name/assets/links/:link_id | ||||
| ``` | ||||
| 
 | ||||
| | Attribute     | Type           | Required | Description                             | | ||||
| | ------------- | -------------- | -------- | --------------------------------------- | | ||||
| | `id`          | integer/string | yes      | The ID or [URL-encoded path of the project](../rest/index.md#namespaced-path-encoding). | | ||||
| | `tag_name`    | string         | yes      | The tag associated with the Release. | | ||||
| | `link_id`     | integer         | yes      | The ID of the link. | | ||||
| | `name`        | string         | no | The name of the link. | | ||||
| | `url`         | string         | no | The URL of the link. | | ||||
| | `filepath` | string     | no | Optional path for a [Direct Asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | ||||
| | `link_type`        | string         | no       | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. | | ||||
| | Attribute            | Type           | Required | Description                                                                                                               | | ||||
| | -------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | | ||||
| | `id`                 | integer/string | yes      | The ID or [URL-encoded path of the project](../rest/index.md#namespaced-path-encoding). | | ||||
| | `tag_name`           | string         | yes      | The tag associated with the Release. | | ||||
| | `link_id`            | integer        | yes      | The ID of the link. | | ||||
| | `name`               | string         | no       | The name of the link. | | ||||
| | `url`                | string         | no       | The URL of the link. | | ||||
| | `filepath`           | string         | no       | Deprecated: Use `direct_asset_path` instead. | | ||||
| | `direct_asset_path`  | string         | no       | Optional path for a [direct asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets). | | ||||
| | `link_type`          | string         | no       | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. | | ||||
| 
 | ||||
| NOTE: | ||||
| You have to specify at least one of `name` or `url` | ||||
|  |  | |||
|  | @ -214,15 +214,6 @@ In order to disable the warning use `RSPEC_WARN_MISSING_FEATURE_CATEGORY=false` | |||
| RSPEC_WARN_MISSING_FEATURE_CATEGORY=false bin/rspec spec/<test_file> | ||||
| ``` | ||||
| 
 | ||||
| ### Excluding specs from feature categorization | ||||
| 
 | ||||
| In the rare case an action cannot be tied to a feature category this | ||||
| can be done using the `not_owned` feature category. | ||||
| 
 | ||||
| ```ruby | ||||
| RSpec.describe Utils, feature_category: :not_owned do | ||||
| ``` | ||||
| 
 | ||||
| ### Tooling feature category | ||||
| 
 | ||||
| For Engineering Productivity internal tooling we use `feature_category: :tooling`. | ||||
|  |  | |||
|  | @ -61,6 +61,38 @@ To enable Arkose Protect: | |||
|    Feature.enable(:arkose_labs_prevent_login) | ||||
|    ``` | ||||
| 
 | ||||
| ## Triage and debug ArkoseLabs issues | ||||
| 
 | ||||
| You can triage and debug issues raised by ArkoseLabs with: | ||||
| 
 | ||||
| - The [GitLab production logs](https://log.gprd.gitlab.net). | ||||
| - The [Arkose logging service](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/arkose/logger.rb). | ||||
| 
 | ||||
| ### View ArkoseLabs Verify API response for a user session | ||||
| 
 | ||||
| To view an ArkoseLabs Verify API response for a user, [query the GitLab production logs](https://log.gprd.gitlab.net/goto/54b82f50-935a-11ed-9f43-e3784d7fe3ca) with the following KQL: | ||||
| 
 | ||||
| ```plaintext | ||||
| KQL: json.message:"Arkose verify response" AND json.username:replace_username_here | ||||
| ``` | ||||
| 
 | ||||
| If the query is valid, the result contains debug information about the user's session: | ||||
| 
 | ||||
| | Response | Description | | ||||
| |---------|-------------| | ||||
| | `json.response.session_details.suppressed` | Value is `true` if the challenge was not shown to the user. Always `true` if the user is allowlisted. | | ||||
| | `json.arkose.risk_band` | Can be `low`, `medium`, or `high`. Ignored on sign in. Use to debug identity verification issues. | | ||||
| | `json.response.session_details.solved` | Indicates whether the user solved the challenge. Always `true` if the user is allowlisted. | | ||||
| | `json.response.session_details.previously_verified` | Indicates whether the token has been reused. Default is `false`. If `true`, it might indicate malicious activity. | | ||||
| 
 | ||||
| ### Check if a user failed an ArkoseLabs challenge | ||||
| 
 | ||||
| To check if a user failed to sign in because the ArkoseLabs challenge was not solved, [query the GitLab production logs](https://log.gprd.gitlab.net/goto/b97c8a80-935a-11ed-85ed-e7557b0a598c) with the following KQL: | ||||
| 
 | ||||
| ```plaintext | ||||
| KQL: json.message:"Challenge was not solved" AND json.username:replace_username_here` | ||||
| ``` | ||||
| 
 | ||||
| ## QA tests caveat | ||||
| 
 | ||||
| Several GitLab QA test suites need to sign in to the app to test its features. This can conflict | ||||
|  |  | |||
|  | @ -0,0 +1,32 @@ | |||
| --- | ||||
| type: reference, howto | ||||
| stage: Manage | ||||
| group: Authentication and Authorization | ||||
| info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments | ||||
| --- | ||||
| 
 | ||||
| # Configure SCIM for self-managed GitLab instances **(PREMIUM SELF)** | ||||
| 
 | ||||
| You can use the open standard System for Cross-domain Identity Management (SCIM) to automatically: | ||||
| 
 | ||||
| - Create users. | ||||
| - Remove users (deactivate SCIM identity). | ||||
| 
 | ||||
| The [internal GitLab SCIM API](../../../development/internal_api/index.md#instance-scim-api) implements part of [the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). | ||||
| 
 | ||||
| If you are a GitLab.com user, see [configuring SCIM for GitLab.com groups](../../../user/group/saml_sso/scim_setup.md). | ||||
| 
 | ||||
| ## Configure GitLab | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Configure [SAML single sign-on](../../../integration/saml.md). | ||||
| 
 | ||||
| To configure GitLab SCIM: | ||||
| 
 | ||||
| 1. On the top bar, select **Main menu > Admin area**. | ||||
| 1. On the left sidebar, select **Settings > General**. | ||||
| 1. Expand the **SCIM Token** section and select **Generate a SCIM token**. | ||||
| 1. For configuration of your identity provider, save the: | ||||
|     - Token from the **Your SCIM token** field. | ||||
|     - URL from the **SCIM API endpoint URL** field. | ||||
|  | @ -95,12 +95,35 @@ Our criteria for the separation of duties is as follows: | |||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213364) in GitLab 13.3. | ||||
| > - Chain of Custody reports sent using email [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/342594) in GitLab 15.3 with a flag named `async_chain_of_custody_report`. Disabled by default. | ||||
| > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/370100) in GitLab 15.5. Feature flag `async_chain_of_custody_report` removed. | ||||
| > - Chain of Custody report includes all commits (instead of just merge commits) [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267601) in GitLab 15.8 with a flag named `all_commits_compliance_report`. Disabled by default. | ||||
| 
 | ||||
| FLAG: | ||||
| On self-managed GitLab, by default the Chain of Custody report only contains information on merge commits. To make the report contain information on all commits to projects within a group, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `all_commits_compliance_report`. On GitLab.com, this feature is not available. | ||||
| 
 | ||||
| The Chain of Custody report allows customers to export a list of merge commits within the group. | ||||
| The data provides a comprehensive view with respect to merge commits. It includes the merge commit SHA, | ||||
| merge request author, merge request ID, merge user, date merged, pipeline ID, group name, project name, and merge request approvers. | ||||
| Depending on the merge strategy, the merge commit SHA can be a merge commit, squash commit, or a diff head commit. | ||||
| 
 | ||||
| With the `all_commits_compliance_report` flag enabled, the Chain of Custody report provides a 1 month trailing window of any commit into a project under the group. | ||||
| 
 | ||||
| To generate the report for all commits, GitLab: | ||||
| 
 | ||||
| 1. Fetches all projects under the group. | ||||
| 1. For each project, fetches the last 1 month of commits. Each project is capped at 1024 commits. If there are more than 1024 commits in the 1-month window, they | ||||
|    are truncated. | ||||
| 1. Writes the commits to a CSV file. The file is truncated at 15 MB because the report is emailed as an attachment. | ||||
| 
 | ||||
| The expanded report includes the commit SHA, commit author, committer, date committed, the group, and the project. | ||||
| If the commit has a related merge commit, then the following are also included: | ||||
| 
 | ||||
| - Merge commit SHA. | ||||
| - Merge request ID. | ||||
| - User who merged the merge request. | ||||
| - Merge date. | ||||
| - Pipeline ID. | ||||
| - Merge request approvers. | ||||
| 
 | ||||
| To generate the Chain of Custody report: | ||||
| 
 | ||||
| 1. On the top bar, select **Main menu > Groups** and find your group. | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ module Gitlab | |||
|           'manifest_src' => "'self'", | ||||
|           'media_src' => "'self' data: http: https:", | ||||
|           'script_src' => ContentSecurityPolicy::Directives.script_src, | ||||
|           'style_src' => "'self' 'unsafe-inline'", | ||||
|           'style_src' => ContentSecurityPolicy::Directives.style_src, | ||||
|           'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:", | ||||
|           'object_src' => "'none'", | ||||
|           'report_uri' => nil | ||||
|  | @ -43,6 +43,7 @@ module Gitlab | |||
| 
 | ||||
|         allow_websocket_connections(directives) | ||||
|         allow_cdn(directives, Settings.gitlab.cdn_host) if Settings.gitlab.cdn_host.present? | ||||
|         allow_zuora(directives) if Gitlab.com? | ||||
|         # Support for Sentry setup via configuration files will be removed in 16.0 | ||||
|         # in favor of Gitlab::CurrentSettings. | ||||
|         allow_legacy_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn | ||||
|  | @ -128,6 +129,14 @@ module Gitlab | |||
|         append_to_directive(directives, 'frame_src', cdn_host) | ||||
|       end | ||||
| 
 | ||||
|       def self.zuora_host | ||||
|         "https://*.zuora.com/apps/PublicHostedPageLite.do" | ||||
|       end | ||||
| 
 | ||||
|       def self.allow_zuora(directives) | ||||
|         append_to_directive(directives, 'frame_src', zuora_host) | ||||
|       end | ||||
| 
 | ||||
|       def self.append_to_directive(directives, directive, text) | ||||
|         directives[directive] = "#{directives[directive]} #{text}".strip | ||||
|       end | ||||
|  |  | |||
|  | @ -18,6 +18,10 @@ module Gitlab | |||
|       def self.script_src | ||||
|         "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com" | ||||
|       end | ||||
| 
 | ||||
|       def self.style_src | ||||
|         "'self' 'unsafe-inline'" | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -25137,6 +25137,9 @@ msgstr "" | |||
| msgid "List available repositories" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "List of all commits" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "List of all merge commits" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,8 +32,7 @@ module Gitlab | |||
|           div :project | ||||
|           div :storage_type_legend | ||||
|           span :container_registry_size | ||||
|           div :purchased_usage_total_free # Different UI for free namespace | ||||
|           span :purchased_usage_total | ||||
|           div :purchased_usage_total | ||||
|           div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/ | ||||
|           div :additional_storage_alert, text: /purchase additional storage/ | ||||
| 
 | ||||
|  | @ -66,14 +65,10 @@ module Gitlab | |||
|           # Returns total purchased storage value once it's ready on page | ||||
|           # | ||||
|           # @return [Float] Total purchased storage value in GiB | ||||
|           def total_purchased_storage(free_name_space = true) | ||||
|           def total_purchased_storage | ||||
|             additional_storage_alert_element.wait_until(&:present?) | ||||
| 
 | ||||
|             if free_name_space | ||||
|               purchased_usage_total_free.split('/').last.match(/\d+\.\d+/)[0].to_f | ||||
|             else | ||||
|               purchased_usage_total.to_f | ||||
|             end | ||||
|             purchased_usage_total[/(\d+){2}.\d+/].to_f | ||||
|           end | ||||
| 
 | ||||
|           def additional_ci_minutes_added? | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe DashboardController do | ||||
| RSpec.describe DashboardController, feature_category: :code_review_workflow do | ||||
|   context 'signed in' do | ||||
|     let_it_be(:user) { create(:user) } | ||||
|     let_it_be(:project) { create(:project) } | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: | |||
|   end | ||||
| 
 | ||||
|   before do | ||||
|     stub_feature_flags(refactor_security_extension: false) | ||||
|     project.add_maintainer(user) | ||||
|     project_only_mwps.add_maintainer(user) | ||||
|     sign_in(user) | ||||
|  |  | |||
|  | @ -1,20 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Zuora content security policy', feature_category: :purchase do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:project) { create(:project) } | ||||
|   let(:pipeline) { create(:ci_pipeline, project: project) } | ||||
| 
 | ||||
|   before do | ||||
|     project.add_developer(user) | ||||
|     sign_in(user) | ||||
|   end | ||||
| 
 | ||||
|   it 'has proper Content Security Policy headers' do | ||||
|     visit pipeline_path(pipeline) | ||||
| 
 | ||||
|     expect(response_headers['Content-Security-Policy']).to include('https://*.zuora.com') | ||||
|   end | ||||
| end | ||||
|  | @ -8,6 +8,7 @@ import statisticsLabels from '~/admin/statistics_panel/constants'; | |||
| import createStore from '~/admin/statistics_panel/store'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import mockStatistics from '../mock_data'; | ||||
| 
 | ||||
| Vue.use(Vuex); | ||||
|  | @ -25,7 +26,7 @@ describe('Admin statistics app', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     axiosMock = new AxiosMockAdapter(axios); | ||||
|     axiosMock.onGet(/api\/(.*)\/application\/statistics/).reply(200); | ||||
|     axiosMock.onGet(/api\/(.*)\/application\/statistics/).reply(HTTP_STATUS_OK); | ||||
|     store = createStore(); | ||||
|   }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import testAction from 'helpers/vuex_action_helper'; | |||
| import service from '~/batch_comments/services/drafts_service'; | ||||
| import * as actions from '~/batch_comments/stores/modules/batch_comments/actions'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| describe('Batch comments store actions', () => { | ||||
|   let res = {}; | ||||
|  | @ -31,7 +32,7 @@ describe('Batch comments store actions', () => { | |||
|   describe('addDraftToDiscussion', () => { | ||||
|     it('commits ADD_NEW_DRAFT if no errors returned', () => { | ||||
|       res = { id: 1 }; | ||||
|       mock.onAny().reply(200, res); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK, res); | ||||
| 
 | ||||
|       return testAction( | ||||
|         actions.addDraftToDiscussion, | ||||
|  | @ -58,7 +59,7 @@ describe('Batch comments store actions', () => { | |||
|   describe('createNewDraft', () => { | ||||
|     it('commits ADD_NEW_DRAFT if no errors returned', () => { | ||||
|       res = { id: 1 }; | ||||
|       mock.onAny().reply(200, res); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK, res); | ||||
| 
 | ||||
|       return testAction( | ||||
|         actions.createNewDraft, | ||||
|  | @ -100,7 +101,7 @@ describe('Batch comments store actions', () => { | |||
|         commit, | ||||
|       }; | ||||
|       res = { id: 1 }; | ||||
|       mock.onAny().reply(200); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       return actions.deleteDraft(context, { id: 1 }).then(() => { | ||||
|         expect(commit).toHaveBeenCalledWith('DELETE_DRAFT', 1); | ||||
|  | @ -144,7 +145,7 @@ describe('Batch comments store actions', () => { | |||
|         }, | ||||
|       }; | ||||
|       res = { id: 1 }; | ||||
|       mock.onAny().reply(200, res); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK, res); | ||||
| 
 | ||||
|       return actions.fetchDrafts(context).then(() => { | ||||
|         expect(commit).toHaveBeenCalledWith('SET_BATCH_COMMENTS_DRAFTS', { id: 1 }); | ||||
|  | @ -169,7 +170,7 @@ describe('Batch comments store actions', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('dispatches actions & commits', () => { | ||||
|       mock.onAny().reply(200); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       return actions.publishReview({ dispatch, commit, getters, rootGetters }).then(() => { | ||||
|         expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']); | ||||
|  | @ -180,7 +181,7 @@ describe('Batch comments store actions', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('calls service with notes data', () => { | ||||
|       mock.onAny().reply(200); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK); | ||||
|       jest.spyOn(axios, 'post'); | ||||
| 
 | ||||
|       return actions | ||||
|  | @ -221,7 +222,7 @@ describe('Batch comments store actions', () => { | |||
|         commit, | ||||
|       }; | ||||
|       res = { id: 1 }; | ||||
|       mock.onAny().reply(200, res); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK, res); | ||||
|       params = { note: { id: 1 }, noteText: 'test' }; | ||||
|     }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import axios from 'axios'; | |||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | ||||
| import renderOpenApi from '~/blob/openapi'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| describe('OpenAPI blob viewer', () => { | ||||
|   const id = 'js-openapi-viewer'; | ||||
|  | @ -10,7 +11,7 @@ describe('OpenAPI blob viewer', () => { | |||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     setHTMLFixture(`<div id="${id}" data-endpoint="${mockEndpoint}"></div>`); | ||||
|     mock = new MockAdapter(axios).onGet().reply(200); | ||||
|     mock = new MockAdapter(axios).onGet().reply(HTTP_STATUS_OK); | ||||
|     await renderOpenApi(); | ||||
|   }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | |||
| import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; | ||||
| import Clusters from '~/clusters/clusters_bundle'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import initProjectSelectDropdown from '~/project_select'; | ||||
| 
 | ||||
| jest.mock('~/lib/utils/poll'); | ||||
|  | @ -19,7 +20,7 @@ describe('Clusters', () => { | |||
| 
 | ||||
|     mock = new MockAdapter(axios); | ||||
| 
 | ||||
|     mock.onGet(statusPath).reply(200); | ||||
|     mock.onGet(statusPath).reply(HTTP_STATUS_OK); | ||||
|   }; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; | |||
| import testAction from 'helpers/vuex_action_helper'; | ||||
| import * as actions from '~/emoji/awards_app/store/actions'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| jest.mock('@sentry/browser'); | ||||
| jest.mock('~/vue_shared/plugins/global_toast'); | ||||
|  | @ -152,7 +153,7 @@ describe('Awards app actions', () => { | |||
| 
 | ||||
|         describe('success', () => { | ||||
|           beforeEach(() => { | ||||
|             mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(200); | ||||
|             mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(HTTP_STATUS_OK); | ||||
|           }); | ||||
| 
 | ||||
|           it('commits REMOVE_AWARD', async () => { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import { s__ } from '~/locale'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { resolvers } from '~/environments/graphql/resolvers'; | ||||
| import environmentToRollback from '~/environments/graphql/queries/environment_to_rollback.query.graphql'; | ||||
| import environmentToDelete from '~/environments/graphql/queries/environment_to_delete.query.graphql'; | ||||
|  | @ -44,7 +45,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|       const search = ''; | ||||
|       mock | ||||
|         .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search } }) | ||||
|         .reply(200, environmentsApp, {}); | ||||
|         .reply(HTTP_STATUS_OK, environmentsApp, {}); | ||||
| 
 | ||||
|       const app = await mockResolvers.Query.environmentApp( | ||||
|         null, | ||||
|  | @ -63,7 +64,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|       const interval = 3000; | ||||
|       mock | ||||
|         .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) | ||||
|         .reply(200, environmentsApp, { | ||||
|         .reply(HTTP_STATUS_OK, environmentsApp, { | ||||
|           'poll-interval': interval, | ||||
|         }); | ||||
| 
 | ||||
|  | @ -78,7 +79,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|       const scope = 'stopped'; | ||||
|       mock | ||||
|         .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) | ||||
|         .reply(200, environmentsApp, { | ||||
|         .reply(HTTP_STATUS_OK, environmentsApp, { | ||||
|           'x-next-page': '2', | ||||
|           'x-page': '1', | ||||
|           'X-Per-Page': '2', | ||||
|  | @ -108,7 +109,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|       const scope = 'stopped'; | ||||
|       mock | ||||
|         .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) | ||||
|         .reply(200, environmentsApp, {}); | ||||
|         .reply(HTTP_STATUS_OK, environmentsApp, {}); | ||||
| 
 | ||||
|       await mockResolvers.Query.environmentApp(null, { scope, page: 1, search: '' }, { cache }); | ||||
|       expect(cache.writeQuery).toHaveBeenCalledWith({ | ||||
|  | @ -131,7 +132,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|     it('should fetch the folder url passed to it', async () => { | ||||
|       mock | ||||
|         .onGet(ENDPOINT, { params: { per_page: 3, scope: 'available', search: '' } }) | ||||
|         .reply(200, folder); | ||||
|         .reply(HTTP_STATUS_OK, folder); | ||||
| 
 | ||||
|       const environmentFolder = await mockResolvers.Query.folder(null, { | ||||
|         environment: { folderPath: ENDPOINT }, | ||||
|  | @ -144,7 +145,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|   }); | ||||
|   describe('stopEnvironment', () => { | ||||
|     it('should post to the stop environment path', async () => { | ||||
|       mock.onPost(ENDPOINT).reply(200); | ||||
|       mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       const client = { writeQuery: jest.fn() }; | ||||
|       const environment = { stopPath: ENDPOINT }; | ||||
|  | @ -180,7 +181,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|   }); | ||||
|   describe('rollbackEnvironment', () => { | ||||
|     it('should post to the retry environment path', async () => { | ||||
|       mock.onPost(ENDPOINT).reply(200); | ||||
|       mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       await mockResolvers.Mutation.rollbackEnvironment(null, { | ||||
|         environment: { retryUrl: ENDPOINT }, | ||||
|  | @ -193,7 +194,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|   }); | ||||
|   describe('deleteEnvironment', () => { | ||||
|     it('should DELETE to the delete environment path', async () => { | ||||
|       mock.onDelete(ENDPOINT).reply(200); | ||||
|       mock.onDelete(ENDPOINT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       await mockResolvers.Mutation.deleteEnvironment(null, { | ||||
|         environment: { deletePath: ENDPOINT }, | ||||
|  | @ -206,7 +207,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|   }); | ||||
|   describe('cancelAutoStop', () => { | ||||
|     it('should post to the auto stop path', async () => { | ||||
|       mock.onPost(ENDPOINT).reply(200); | ||||
|       mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       await mockResolvers.Mutation.cancelAutoStop(null, { autoStopUrl: ENDPOINT }); | ||||
| 
 | ||||
|  | @ -262,7 +263,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { | |||
|   }); | ||||
|   describe('action', () => { | ||||
|     it('should POST to the given path', async () => { | ||||
|       mock.onPost(ENDPOINT).reply(200); | ||||
|       mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); | ||||
|       const errors = await mockResolvers.Mutation.action(null, { action: { playPath: ENDPOINT } }); | ||||
| 
 | ||||
|       expect(errors).toEqual({ __typename: 'LocalEnvironmentErrors', errors: [] }); | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import * as types from '~/error_tracking_settings/store/mutation_types'; | |||
| import defaultState from '~/error_tracking_settings/store/state'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { refreshCurrentPage } from '~/lib/utils/url_utility'; | ||||
| import { projectList } from '../mock'; | ||||
| 
 | ||||
|  | @ -118,7 +119,7 @@ describe('error tracking settings actions', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('should save the page', async () => { | ||||
|       mock.onPatch(TEST_HOST).reply(200); | ||||
|       mock.onPatch(TEST_HOST).reply(HTTP_STATUS_OK); | ||||
|       await testAction(actions.updateSettings, null, state, [], [{ type: 'requestSettings' }]); | ||||
|       expect(mock.history.patch.length).toBe(1); | ||||
|       expect(refreshCurrentPage).toHaveBeenCalled(); | ||||
|  |  | |||
|  | @ -2,13 +2,14 @@ import axios from 'axios'; | |||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | ||||
| import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| describe('Filtered Search Dropdown Manager', () => { | ||||
|   let mock; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mock = new MockAdapter(axios); | ||||
|     mock.onGet().reply(200); | ||||
|     mock.onGet().reply(HTTP_STATUS_OK); | ||||
|   }); | ||||
| 
 | ||||
|   describe('addWordToInput', () => { | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | |||
| import FilteredSearchSpecHelper from 'helpers/filtered_search_spec_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants'; | ||||
| 
 | ||||
| describe('Filtered Search Visual Tokens', () => { | ||||
|  | @ -24,7 +25,7 @@ describe('Filtered Search Visual Tokens', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mock = new MockAdapter(axios); | ||||
|     mock.onGet().reply(200); | ||||
|     mock.onGet().reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|     setHTMLFixture(` | ||||
|       <ul class="tokens-container"> | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import Api from '~/api'; | |||
| import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql'; | ||||
| import services from '~/ide/services'; | ||||
| import { query, mutate } from '~/ide/services/gql'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { escapeFileUrl } from '~/lib/utils/url_utility'; | ||||
| import ciConfig from '~/ci/pipeline_editor/graphql/queries/ci_config.query.graphql'; | ||||
| import { projectData } from '../mock_data'; | ||||
|  | @ -271,7 +272,7 @@ describe('IDE services', () => { | |||
|       const TEST_PROJECT_PATH = 'foo/bar'; | ||||
|       const axiosURL = `${TEST_RELATIVE_URL_ROOT}/${TEST_PROJECT_PATH}/service_ping/web_ide_pipelines_count`; | ||||
| 
 | ||||
|       mock.onPost(axiosURL).reply(200); | ||||
|       mock.onPost(axiosURL).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       return services.pingUsage(TEST_PROJECT_PATH).then(() => { | ||||
|         expect(axios.post).toHaveBeenCalledWith(axiosURL); | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import { | |||
| } from '~/ide/stores/actions'; | ||||
| import * as types from '~/ide/stores/mutation_types'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_IM_A_TEAPOT } from '~/lib/utils/http_status'; | ||||
| import { visitUrl } from '~/lib/utils/url_utility'; | ||||
| import { file, createTriggerRenameAction, createTriggerChangeAction } from '../helpers'; | ||||
| 
 | ||||
|  | @ -927,7 +928,7 @@ describe('Multi-file store actions', () => { | |||
|       }); | ||||
| 
 | ||||
|       it('does not pass the error further and flashes an alert if error is not 404', async () => { | ||||
|         mock.onGet(/(.*)/).replyOnce(418); | ||||
|         mock.onGet(/(.*)/).replyOnce(HTTP_STATUS_IM_A_TEAPOT); | ||||
| 
 | ||||
|         await expect(getBranchData(...callParams)).rejects.toEqual( | ||||
|           new Error('Branch not loaded - <strong>abc/def/main-testing</strong>'), | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; | |||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; | ||||
| import { createAlert } from '~/flash'; | ||||
| import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status'; | ||||
| import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { STATUSES } from '~/import_entities/constants'; | ||||
| import { i18n, ROOT_NAMESPACE } from '~/import_entities/import_groups/constants'; | ||||
|  | @ -113,7 +113,7 @@ describe('import table', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     axiosMock = new MockAdapter(axios); | ||||
|     axiosMock.onGet(/.*\/exists$/, () => []).reply(200); | ||||
|     axiosMock.onGet(/.*\/exists$/, () => []).reply(HTTP_STATUS_OK); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import { | |||
| import state from '~/import_entities/import_projects/store/state'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| jest.mock('~/flash'); | ||||
| 
 | ||||
|  | @ -433,7 +434,7 @@ describe('import_projects store actions', () => { | |||
|     afterEach(() => mock.restore()); | ||||
| 
 | ||||
|     it('commits CANCEL_IMPORT_SUCCESS on success', async () => { | ||||
|       mock.onPost(MOCK_ENDPOINT).reply(200); | ||||
|       mock.onPost(MOCK_ENDPOINT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       await testAction( | ||||
|         cancelImport, | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { GlEmptyState, GlLink } from '@gitlab/ui'; | |||
| import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; | ||||
| import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue'; | ||||
| import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue'; | ||||
| import NewIssueDropdown from '~/vue_shared/components/new_issue_dropdown/new_issue_dropdown.vue'; | ||||
| import { i18n } from '~/issues/list/constants'; | ||||
| 
 | ||||
| describe('EmptyStateWithoutAnyIssues component', () => { | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/con | |||
| import EmptyStateWithAnyIssues from '~/issues/list/components/empty_state_with_any_issues.vue'; | ||||
| import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue'; | ||||
| import IssuesListApp from '~/issues/list/components/issues_list_app.vue'; | ||||
| import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue'; | ||||
| import NewIssueDropdown from '~/vue_shared/components/new_issue_dropdown/new_issue_dropdown.vue'; | ||||
| import { | ||||
|   CREATED_DESC, | ||||
|   RELATIVE_POSITION, | ||||
|  |  | |||
|  | @ -1,133 +0,0 @@ | |||
| import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; | ||||
| import { mount, shallowMount } from '@vue/test-utils'; | ||||
| import Vue from 'vue'; | ||||
| import VueApollo from 'vue-apollo'; | ||||
| import createMockApollo from 'helpers/mock_apollo_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue'; | ||||
| import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql'; | ||||
| import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; | ||||
| import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; | ||||
| import { | ||||
|   emptySearchProjectsQueryResponse, | ||||
|   project1, | ||||
|   project3, | ||||
|   searchProjectsQueryResponse, | ||||
| } from '../mock_data'; | ||||
| 
 | ||||
| describe('NewIssueDropdown component', () => { | ||||
|   let wrapper; | ||||
| 
 | ||||
|   Vue.use(VueApollo); | ||||
| 
 | ||||
|   const mountComponent = ({ | ||||
|     search = '', | ||||
|     queryResponse = searchProjectsQueryResponse, | ||||
|     mountFn = shallowMount, | ||||
|   } = {}) => { | ||||
|     const requestHandlers = [[searchProjectsQuery, jest.fn().mockResolvedValue(queryResponse)]]; | ||||
|     const apolloProvider = createMockApollo(requestHandlers); | ||||
| 
 | ||||
|     return mountFn(NewIssueDropdown, { | ||||
|       apolloProvider, | ||||
|       provide: { | ||||
|         fullPath: 'mushroom-kingdom', | ||||
|       }, | ||||
|       data() { | ||||
|         return { search }; | ||||
|       }, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const findDropdown = () => wrapper.findComponent(GlDropdown); | ||||
|   const findInput = () => wrapper.findComponent(GlSearchBoxByType); | ||||
|   const showDropdown = async () => { | ||||
|     findDropdown().vm.$emit('shown'); | ||||
|     await waitForPromises(); | ||||
|     jest.advanceTimersByTime(DEBOUNCE_DELAY); | ||||
|     await waitForPromises(); | ||||
|   }; | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     wrapper.destroy(); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a split dropdown', () => { | ||||
|     wrapper = mountComponent(); | ||||
| 
 | ||||
|     expect(findDropdown().props('split')).toBe(true); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a label for the dropdown toggle button', () => { | ||||
|     wrapper = mountComponent(); | ||||
| 
 | ||||
|     expect(findDropdown().attributes('toggle-text')).toBe(NewIssueDropdown.i18n.toggleButtonLabel); | ||||
|   }); | ||||
| 
 | ||||
|   it('focuses on input when dropdown is shown', async () => { | ||||
|     wrapper = mountComponent({ mountFn: mount }); | ||||
| 
 | ||||
|     const inputSpy = jest.spyOn(findInput().vm, 'focusInput'); | ||||
| 
 | ||||
|     await showDropdown(); | ||||
| 
 | ||||
|     expect(inputSpy).toHaveBeenCalledTimes(1); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders projects with issues enabled', async () => { | ||||
|     wrapper = mountComponent({ mountFn: mount }); | ||||
|     await showDropdown(); | ||||
| 
 | ||||
|     const listItems = wrapper.findAll('li'); | ||||
| 
 | ||||
|     expect(listItems.at(0).text()).toBe(project1.nameWithNamespace); | ||||
|     expect(listItems.at(1).text()).toBe(project3.nameWithNamespace); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders `No matches found` when there are no matches', async () => { | ||||
|     wrapper = mountComponent({ | ||||
|       search: 'no matches', | ||||
|       queryResponse: emptySearchProjectsQueryResponse, | ||||
|       mountFn: mount, | ||||
|     }); | ||||
| 
 | ||||
|     await showDropdown(); | ||||
| 
 | ||||
|     expect(wrapper.find('li').text()).toBe(NewIssueDropdown.i18n.noMatchesFound); | ||||
|   }); | ||||
| 
 | ||||
|   describe('when no project is selected', () => { | ||||
|     beforeEach(() => { | ||||
|       wrapper = mountComponent(); | ||||
|     }); | ||||
| 
 | ||||
|     it('dropdown button is not a link', () => { | ||||
|       expect(findDropdown().attributes('split-href')).toBeUndefined(); | ||||
|     }); | ||||
| 
 | ||||
|     it('displays default text on the dropdown button', () => { | ||||
|       expect(findDropdown().props('text')).toBe(NewIssueDropdown.i18n.defaultDropdownText); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('when a project is selected', () => { | ||||
|     beforeEach(async () => { | ||||
|       wrapper = mountComponent({ mountFn: mount }); | ||||
|       await waitForPromises(); | ||||
|       await showDropdown(); | ||||
| 
 | ||||
|       wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1); | ||||
|       await waitForPromises(); | ||||
|     }); | ||||
| 
 | ||||
|     it('dropdown button is a link', () => { | ||||
|       const href = joinPaths(project1.webUrl, DASH_SCOPE, 'issues/new'); | ||||
| 
 | ||||
|       expect(findDropdown().attributes('split-href')).toBe(href); | ||||
|     }); | ||||
| 
 | ||||
|     it('displays project name on the dropdown button', () => { | ||||
|       expect(findDropdown().props('text')).toBe(`New issue in ${project1.name}`); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -343,49 +343,3 @@ export const urlParamsWithSpecialValues = { | |||
|   weight: 'None', | ||||
|   health_status: 'None', | ||||
| }; | ||||
| 
 | ||||
| export const project1 = { | ||||
|   id: 'gid://gitlab/Group/26', | ||||
|   issuesEnabled: true, | ||||
|   name: 'Super Mario Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Super Mario Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/super-mario-project', | ||||
| }; | ||||
| 
 | ||||
| export const project2 = { | ||||
|   id: 'gid://gitlab/Group/59', | ||||
|   issuesEnabled: false, | ||||
|   name: 'Mario Kart Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Mario Kart Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/mario-kart-project', | ||||
| }; | ||||
| 
 | ||||
| export const project3 = { | ||||
|   id: 'gid://gitlab/Group/103', | ||||
|   issuesEnabled: true, | ||||
|   name: 'Mario Party Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Mario Party Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/mario-party-project', | ||||
| }; | ||||
| 
 | ||||
| export const searchProjectsQueryResponse = { | ||||
|   data: { | ||||
|     group: { | ||||
|       id: '1', | ||||
|       projects: { | ||||
|         nodes: [project1, project2, project3], | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const emptySearchProjectsQueryResponse = { | ||||
|   data: { | ||||
|     group: { | ||||
|       id: '1', | ||||
|       projects: { | ||||
|         nodes: [], | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  |  | |||
|  | @ -3,11 +3,12 @@ import waitForPromises from 'helpers/wait_for_promises'; | |||
| import { initIssueApp } from '~/issues/show'; | ||||
| import * as parseData from '~/issues/show/utils/parse_data'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import createStore from '~/notes/stores'; | ||||
| import { appProps } from './mock_data/mock_data'; | ||||
| 
 | ||||
| const mock = new MockAdapter(axios); | ||||
| mock.onGet().reply(200); | ||||
| mock.onGet().reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
| jest.mock('~/lib/utils/poll'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,14 +3,15 @@ | |||
| import AxiosMockAdapter from 'axios-mock-adapter'; | ||||
| 
 | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| 
 | ||||
| describe('axios_utils', () => { | ||||
|   let mock; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mock = new AxiosMockAdapter(axios); | ||||
|     mock.onAny('/ok').reply(200); | ||||
|     mock.onAny('/err').reply(500); | ||||
|     mock.onAny('/ok').reply(HTTP_STATUS_OK); | ||||
|     mock.onAny('/err').reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); | ||||
|     // eslint-disable-next-line jest/no-standalone-expect
 | ||||
|     expect(axios.countActiveRequests()).toBe(0); | ||||
|   }); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import { TEST_HOST } from 'helpers/test_constants'; | |||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import DeleteMilestoneModal from '~/milestones/components/delete_milestone_modal.vue'; | ||||
| import eventHub from '~/milestones/event_hub'; | ||||
| import { HTTP_STATUS_IM_A_TEAPOT, HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status'; | ||||
| import { redirectTo } from '~/lib/utils/url_utility'; | ||||
| import { createAlert } from '~/flash'; | ||||
| 
 | ||||
|  | @ -71,9 +72,9 @@ describe('Delete milestone modal', () => { | |||
|     }); | ||||
| 
 | ||||
|     it.each` | ||||
|       statusCode | alertMessage | ||||
|       ${418}     | ${`Failed to delete milestone ${mockProps.milestoneTitle}`} | ||||
|       ${404}     | ${`Milestone ${mockProps.milestoneTitle} was not found`} | ||||
|       statusCode                 | alertMessage | ||||
|       ${HTTP_STATUS_IM_A_TEAPOT} | ${`Failed to delete milestone ${mockProps.milestoneTitle}`} | ||||
|       ${HTTP_STATUS_NOT_FOUND}   | ${`Milestone ${mockProps.milestoneTitle} was not found`} | ||||
|     `(
 | ||||
|       'displays error if deleting milestone failed with code $statusCode', | ||||
|       async ({ statusCode, alertMessage }) => { | ||||
|  |  | |||
|  | @ -918,7 +918,7 @@ describe('Monitoring store actions', () => { | |||
| 
 | ||||
|     it('stars dashboard if it is not starred', () => { | ||||
|       state.selectedDashboard = unstarredDashboard; | ||||
|       mock.onPost(unstarredDashboard.user_starred_path).reply(200); | ||||
|       mock.onPost(unstarredDashboard.user_starred_path).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       return testAction(toggleStarredValue, null, state, [ | ||||
|         { type: types.REQUEST_DASHBOARD_STARRING }, | ||||
|  | @ -934,7 +934,7 @@ describe('Monitoring store actions', () => { | |||
| 
 | ||||
|     it('unstars dashboard if it is starred', () => { | ||||
|       state.selectedDashboard = starredDashboard; | ||||
|       mock.onPost(starredDashboard.user_starred_path).reply(200); | ||||
|       mock.onPost(starredDashboard.user_starred_path).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       return testAction(toggleStarredValue, null, state, [ | ||||
|         { type: types.REQUEST_DASHBOARD_STARRING }, | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; | |||
| import { getByText as getByTextHelper } from '@testing-library/dom'; | ||||
| import { GlToggle } from '@gitlab/ui'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; | ||||
| import NewNavToggle from '~/nav/components/new_nav_toggle.vue'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
|  | @ -74,7 +75,7 @@ describe('NewNavToggle', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('reloads the page on success', async () => { | ||||
|       mock.onPut(TEST_ENDPONT).reply(200); | ||||
|       mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       actFn(); | ||||
|       await waitForPromises(); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import { mount } from '@vue/test-utils'; | |||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import CiIcon from '~/vue_shared/components/ci_icon.vue'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue'; | ||||
| import eventHub from '~/pipelines/event_hub'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
|  | @ -188,8 +189,8 @@ describe('Pipelines stage component', () => { | |||
| 
 | ||||
|   describe('job update in dropdown', () => { | ||||
|     beforeEach(async () => { | ||||
|       mock.onGet(dropdownPath).reply(200, stageReply); | ||||
|       mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200); | ||||
|       mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply); | ||||
|       mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|       createComponent(); | ||||
|       await waitForPromises(); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import MockAdapter from 'axios-mock-adapter'; | |||
| import { nextTick } from 'vue'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import ActionComponent from '~/pipelines/components/jobs_shared/action_component.vue'; | ||||
| 
 | ||||
| describe('pipeline graph action component', () => { | ||||
|  | @ -15,7 +16,7 @@ describe('pipeline graph action component', () => { | |||
|   beforeEach(() => { | ||||
|     mock = new MockAdapter(axios); | ||||
| 
 | ||||
|     mock.onPost('foo.json').reply(200); | ||||
|     mock.onPost('foo.json').reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|     wrapper = mount(ActionComponent, { | ||||
|       propsData: { | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises'; | |||
| import { TEST_HOST } from 'spec/test_constants'; | ||||
| import { createAlert } from '~/flash'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; | ||||
| import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue'; | ||||
| import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; | ||||
|  | @ -70,7 +71,7 @@ describe('Pipelines Actions dropdown', () => { | |||
| 
 | ||||
|     describe('on click', () => { | ||||
|       it('makes a request and toggles the loading state', async () => { | ||||
|         mock.onPost(mockActions.path).reply(200); | ||||
|         mock.onPost(mockActions.path).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|         findAllDropdownItems().at(0).vm.$emit('click'); | ||||
| 
 | ||||
|  | @ -132,7 +133,7 @@ describe('Pipelines Actions dropdown', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('makes post request after confirming', async () => { | ||||
|       mock.onPost(scheduledJobAction.path).reply(200); | ||||
|       mock.onPost(scheduledJobAction.path).reply(HTTP_STATUS_OK); | ||||
|       confirmAction.mockResolvedValueOnce(true); | ||||
| 
 | ||||
|       findAllDropdownItems().at(0).vm.$emit('click'); | ||||
|  | @ -145,7 +146,7 @@ describe('Pipelines Actions dropdown', () => { | |||
|     }); | ||||
| 
 | ||||
|     it('does not make post request if confirmation is cancelled', async () => { | ||||
|       mock.onPost(scheduledJobAction.path).reply(200); | ||||
|       mock.onPost(scheduledJobAction.path).reply(HTTP_STATUS_OK); | ||||
|       confirmAction.mockResolvedValueOnce(false); | ||||
| 
 | ||||
|       findAllDropdownItems().at(0).vm.$emit('click'); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import MockAxiosAdapter from 'axios-mock-adapter'; | |||
| import { nextTick } from 'vue'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import SharedRunnersToggleComponent from '~/projects/settings/components/shared_runners_toggle.vue'; | ||||
| 
 | ||||
| const TEST_UPDATE_PATH = '/test/update_shared_runners'; | ||||
|  | @ -36,7 +37,7 @@ describe('projects/settings/components/shared_runners', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mockAxios = new MockAxiosAdapter(axios); | ||||
|     mockAxios.onPost(TEST_UPDATE_PATH).reply(200); | ||||
|     mockAxios.onPost(TEST_UPDATE_PATH).reply(HTTP_STATUS_OK); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import axios from 'axios'; | |||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import Vue, { nextTick } from 'vue'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import MemoryUsage from '~/vue_merge_request_widget/components/deployment/memory_usage.vue'; | ||||
| import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service'; | ||||
| 
 | ||||
|  | @ -66,7 +67,7 @@ describe('MemoryUsage', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mock = new MockAdapter(axios); | ||||
|     mock.onGet(`${url}.json`).reply(200); | ||||
|     mock.onGet(`${url}.json`).reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|     vm = createComponent(); | ||||
|     el = vm.$el; | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import waitForPromises from 'helpers/wait_for_promises'; | |||
| import { securityReportMergeRequestDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data'; | ||||
| import api from '~/api'; | ||||
| import axios from '~/lib/utils/axios_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import Poll from '~/lib/utils/poll'; | ||||
| import { setFaviconOverlay } from '~/lib/utils/favicon'; | ||||
| import notify from '~/lib/utils/notify'; | ||||
|  | @ -837,7 +838,7 @@ describe('MrWidgetOptions', () => { | |||
| 
 | ||||
|   describe('suggestPipeline', () => { | ||||
|     beforeEach(() => { | ||||
|       mock.onAny().reply(200); | ||||
|       mock.onAny().reply(HTTP_STATUS_OK); | ||||
|     }); | ||||
| 
 | ||||
|     describe('given feature flag is enabled', () => { | ||||
|  |  | |||
|  | @ -0,0 +1,57 @@ | |||
| export const emptySearchProjectsQueryResponse = { | ||||
|   data: { | ||||
|     projects: { | ||||
|       nodes: [], | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const emptySearchProjectsWithinGroupQueryResponse = { | ||||
|   data: { | ||||
|     group: { | ||||
|       id: '1', | ||||
|       projects: emptySearchProjectsQueryResponse.data.projects, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const project1 = { | ||||
|   id: 'gid://gitlab/Group/26', | ||||
|   issuesEnabled: true, | ||||
|   name: 'Super Mario Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Super Mario Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/super-mario-project', | ||||
| }; | ||||
| 
 | ||||
| export const project2 = { | ||||
|   id: 'gid://gitlab/Group/59', | ||||
|   issuesEnabled: false, | ||||
|   name: 'Mario Kart Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Mario Kart Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/mario-kart-project', | ||||
| }; | ||||
| 
 | ||||
| export const project3 = { | ||||
|   id: 'gid://gitlab/Group/103', | ||||
|   issuesEnabled: true, | ||||
|   name: 'Mario Party Project', | ||||
|   nameWithNamespace: 'Mushroom Kingdom / Mario Party Project', | ||||
|   webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/mario-party-project', | ||||
| }; | ||||
| 
 | ||||
| export const searchProjectsQueryResponse = { | ||||
|   data: { | ||||
|     projects: { | ||||
|       nodes: [project1, project2, project3], | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const searchProjectsWithinGroupQueryResponse = { | ||||
|   data: { | ||||
|     group: { | ||||
|       id: '1', | ||||
|       projects: searchProjectsQueryResponse.data.projects, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | @ -0,0 +1,149 @@ | |||
| import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; | ||||
| import { mount, shallowMount } from '@vue/test-utils'; | ||||
| import Vue from 'vue'; | ||||
| import VueApollo from 'vue-apollo'; | ||||
| import createMockApollo from 'helpers/mock_apollo_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import NewIssueDropdown from '~/vue_shared/components/new_issue_dropdown/new_issue_dropdown.vue'; | ||||
| import searchUserProjectsQuery from '~/vue_shared/components/new_issue_dropdown/graphql/search_user_projects.query.graphql'; | ||||
| import searchProjectsWithinGroupQuery from '~/issues/list/queries/search_projects.query.graphql'; | ||||
| import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; | ||||
| import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; | ||||
| import { | ||||
|   emptySearchProjectsQueryResponse, | ||||
|   emptySearchProjectsWithinGroupQueryResponse, | ||||
|   project1, | ||||
|   project3, | ||||
|   searchProjectsQueryResponse, | ||||
|   searchProjectsWithinGroupQueryResponse, | ||||
| } from './mock_data'; | ||||
| 
 | ||||
| describe('NewIssueDropdown component', () => { | ||||
|   let wrapper; | ||||
| 
 | ||||
|   Vue.use(VueApollo); | ||||
| 
 | ||||
|   // Props
 | ||||
|   const withinGroupProps = { | ||||
|     query: searchProjectsWithinGroupQuery, | ||||
|     queryVariables: { fullPath: 'mushroom-kingdom' }, | ||||
|     extractProjects: (data) => data.group.projects.nodes, | ||||
|   }; | ||||
| 
 | ||||
|   const mountComponent = ({ | ||||
|     search = '', | ||||
|     query = searchUserProjectsQuery, | ||||
|     queryResponse = searchProjectsQueryResponse, | ||||
|     mountFn = shallowMount, | ||||
|     propsData = {}, | ||||
|   } = {}) => { | ||||
|     const requestHandlers = [[query, jest.fn().mockResolvedValue(queryResponse)]]; | ||||
|     const apolloProvider = createMockApollo(requestHandlers); | ||||
| 
 | ||||
|     return mountFn(NewIssueDropdown, { | ||||
|       apolloProvider, | ||||
|       propsData, | ||||
|       data() { | ||||
|         return { search }; | ||||
|       }, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const findDropdown = () => wrapper.findComponent(GlDropdown); | ||||
|   const findInput = () => wrapper.findComponent(GlSearchBoxByType); | ||||
|   const showDropdown = async () => { | ||||
|     findDropdown().vm.$emit('shown'); | ||||
|     await waitForPromises(); | ||||
|     jest.advanceTimersByTime(DEBOUNCE_DELAY); | ||||
|     await waitForPromises(); | ||||
|   }; | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     wrapper.destroy(); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a split dropdown', () => { | ||||
|     wrapper = mountComponent(); | ||||
| 
 | ||||
|     expect(findDropdown().props('split')).toBe(true); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a label for the dropdown toggle button', () => { | ||||
|     wrapper = mountComponent(); | ||||
| 
 | ||||
|     expect(findDropdown().attributes('toggle-text')).toBe(NewIssueDropdown.i18n.toggleButtonLabel); | ||||
|   }); | ||||
| 
 | ||||
|   it('focuses on input when dropdown is shown', async () => { | ||||
|     wrapper = mountComponent({ mountFn: mount }); | ||||
| 
 | ||||
|     const inputSpy = jest.spyOn(findInput().vm, 'focusInput'); | ||||
| 
 | ||||
|     await showDropdown(); | ||||
| 
 | ||||
|     expect(inputSpy).toHaveBeenCalledTimes(1); | ||||
|   }); | ||||
| 
 | ||||
|   describe.each` | ||||
|     description         | propsData           | query                             | queryResponse                             | emptyResponse | ||||
|     ${'by default'}     | ${undefined}        | ${searchUserProjectsQuery}        | ${searchProjectsQueryResponse}            | ${emptySearchProjectsQueryResponse} | ||||
|     ${'within a group'} | ${withinGroupProps} | ${searchProjectsWithinGroupQuery} | ${searchProjectsWithinGroupQueryResponse} | ${emptySearchProjectsWithinGroupQueryResponse} | ||||
|   `('$description', ({ propsData, query, queryResponse, emptyResponse }) => {
 | ||||
|     it('renders projects with issues enabled', async () => { | ||||
|       wrapper = mountComponent({ mountFn: mount, query, queryResponse, propsData }); | ||||
|       await showDropdown(); | ||||
| 
 | ||||
|       const listItems = wrapper.findAll('li'); | ||||
| 
 | ||||
|       expect(listItems.at(0).text()).toBe(project1.nameWithNamespace); | ||||
|       expect(listItems.at(1).text()).toBe(project3.nameWithNamespace); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders `No matches found` when there are no matches', async () => { | ||||
|       wrapper = mountComponent({ | ||||
|         search: 'no matches', | ||||
|         query, | ||||
|         queryResponse: emptyResponse, | ||||
|         mountFn: mount, | ||||
|         propsData, | ||||
|       }); | ||||
| 
 | ||||
|       await showDropdown(); | ||||
| 
 | ||||
|       expect(wrapper.find('li').text()).toBe(NewIssueDropdown.i18n.noMatchesFound); | ||||
|     }); | ||||
| 
 | ||||
|     describe('when no project is selected', () => { | ||||
|       beforeEach(() => { | ||||
|         wrapper = mountComponent({ query, queryResponse, propsData }); | ||||
|       }); | ||||
| 
 | ||||
|       it('dropdown button is not a link', () => { | ||||
|         expect(findDropdown().attributes('split-href')).toBeUndefined(); | ||||
|       }); | ||||
| 
 | ||||
|       it('displays default text on the dropdown button', () => { | ||||
|         expect(findDropdown().props('text')).toBe(NewIssueDropdown.i18n.defaultDropdownText); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('when a project is selected', () => { | ||||
|       beforeEach(async () => { | ||||
|         wrapper = mountComponent({ mountFn: mount, query, queryResponse, propsData }); | ||||
|         await showDropdown(); | ||||
| 
 | ||||
|         wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1); | ||||
|       }); | ||||
| 
 | ||||
|       it('dropdown button is a link', () => { | ||||
|         const href = joinPaths(project1.webUrl, DASH_SCOPE, 'issues/new'); | ||||
| 
 | ||||
|         expect(findDropdown().attributes('split-href')).toBe(href); | ||||
|       }); | ||||
| 
 | ||||
|       it('displays project name on the dropdown button', () => { | ||||
|         expect(findDropdown().props('text')).toBe(`New issue in ${project1.name}`); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -6,6 +6,7 @@ import Mousetrap from 'mousetrap'; | |||
| import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | ||||
| import GLForm from '~/gl_form'; | ||||
| import * as utils from '~/lib/utils/common_utils'; | ||||
| import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; | ||||
| import ZenMode from '~/zen_mode'; | ||||
| 
 | ||||
| describe('ZenMode', () => { | ||||
|  | @ -32,7 +33,7 @@ describe('ZenMode', () => { | |||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mock = new MockAdapter(axios); | ||||
|     mock.onGet().reply(200); | ||||
|     mock.onGet().reply(HTTP_STATUS_OK); | ||||
| 
 | ||||
|     loadHTMLFixture(fixtureName); | ||||
| 
 | ||||
|  |  | |||
|  | @ -94,13 +94,31 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do | |||
| 
 | ||||
|       it 'adds CDN host to CSP' do | ||||
|         expect(directives['script_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.script_src + " https://cdn.example.com") | ||||
|         expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://cdn.example.com") | ||||
|         expect(directives['style_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.style_src + " https://cdn.example.com") | ||||
|         expect(directives['font_src']).to eq("'self' https://cdn.example.com") | ||||
|         expect(directives['worker_src']).to eq('http://localhost/assets/ blob: data: https://cdn.example.com') | ||||
|         expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/") | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe 'Zuora directives' do | ||||
|       context 'when is Gitlab.com?' do | ||||
|         before do | ||||
|           allow(::Gitlab).to receive(:com?).and_return(true) | ||||
|         end | ||||
| 
 | ||||
|         it 'adds Zuora host to CSP' do | ||||
|           expect(directives['frame_src']).to include('https://*.zuora.com/apps/PublicHostedPageLite.do') | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'when is not Gitlab.com?' do | ||||
|         it 'does not add Zuora host to CSP' do | ||||
|           expect(directives['frame_src']).not_to include('https://*.zuora.com/apps/PublicHostedPageLite.do') | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when sentry is configured' do | ||||
|       let(:legacy_dsn) { 'dummy://abc@legacy-sentry.example.com/1' } | ||||
|       let(:dsn) { 'dummy://def@sentry.example.com/2' } | ||||
|  |  | |||
|  | @ -135,6 +135,15 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev | |||
|     let_it_be(:merge_request3) { create(:merge_request, :unique_branches, reviewers: []) } | ||||
|     let_it_be(:merge_request4) { create(:merge_request, :draft_merge_request) } | ||||
| 
 | ||||
|     describe '.preload_target_project_with_namespace' do | ||||
|       subject(:mr) { described_class.preload_target_project_with_namespace.first } | ||||
| 
 | ||||
|       it 'returns MR with the target project\'s namespace preloaded' do | ||||
|         expect(mr.association(:target_project)).to be_loaded | ||||
|         expect(mr.target_project.association(:namespace)).to be_loaded | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe '.review_requested' do | ||||
|       it 'returns MRs that have any review requests' do | ||||
|         expect(described_class.review_requested).to eq([merge_request1, merge_request2]) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ module gitlab.com/gitlab-org/gitlab/workhorse | |||
| go 1.18 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 | ||||
| 	github.com/BurntSushi/toml v1.2.1 | ||||
| 	github.com/FZambia/sentinel v1.1.1 | ||||
| 	github.com/alecthomas/chroma/v2 v2.4.0 | ||||
|  | @ -29,39 +29,40 @@ require ( | |||
| 	gitlab.com/gitlab-org/gitaly/v15 v15.7.0 | ||||
| 	gitlab.com/gitlab-org/golang-archive-zip v0.1.1 | ||||
| 	gitlab.com/gitlab-org/labkit v1.17.0 | ||||
| 	gocloud.dev v0.27.0 | ||||
| 	gocloud.dev v0.28.0 | ||||
| 	golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 | ||||
| 	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 | ||||
| 	golang.org/x/net v0.1.0 | ||||
| 	golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c | ||||
| 	golang.org/x/tools v0.1.12 | ||||
| 	golang.org/x/net v0.4.0 | ||||
| 	golang.org/x/oauth2 v0.2.0 | ||||
| 	golang.org/x/tools v0.2.0 | ||||
| 	google.golang.org/grpc v1.51.0 | ||||
| 	google.golang.org/protobuf v1.28.1 | ||||
| 	honnef.co/go/tools v0.3.3 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go v0.103.0 // indirect | ||||
| 	cloud.google.com/go/compute v1.7.0 // indirect | ||||
| 	cloud.google.com/go/iam v0.3.0 // indirect | ||||
| 	cloud.google.com/go/monitoring v1.5.0 // indirect | ||||
| 	cloud.google.com/go v0.107.0 // indirect | ||||
| 	cloud.google.com/go/compute v1.13.0 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.2.2 // indirect | ||||
| 	cloud.google.com/go/iam v0.7.0 // indirect | ||||
| 	cloud.google.com/go/monitoring v1.9.0 // indirect | ||||
| 	cloud.google.com/go/profiler v0.1.0 // indirect | ||||
| 	cloud.google.com/go/storage v1.24.0 // indirect | ||||
| 	cloud.google.com/go/trace v1.2.0 // indirect | ||||
| 	contrib.go.opencensus.io/exporter/stackdriver v0.13.13 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect | ||||
| 	cloud.google.com/go/storage v1.28.0 // indirect | ||||
| 	cloud.google.com/go/trace v1.4.0 // indirect | ||||
| 	contrib.go.opencensus.io/exporter/stackdriver v0.13.14 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect | ||||
| 	github.com/Azure/go-autorest v14.2.0+incompatible // indirect | ||||
| 	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect | ||||
| 	github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect | ||||
| 	github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect | ||||
| 	github.com/DataDog/datadog-go v4.4.0+incompatible // indirect | ||||
| 	github.com/DataDog/sketches-go v1.0.0 // indirect | ||||
| 	github.com/Microsoft/go-winio v0.5.1 // indirect | ||||
| 	github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect | ||||
| 	github.com/beevik/ntp v0.3.0 // indirect | ||||
| 	github.com/beorn7/perks v1.0.1 // indirect | ||||
| 	github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect | ||||
| 	github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect | ||||
| 	github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect | ||||
| 	github.com/cespare/xxhash/v2 v2.1.2 // indirect | ||||
| 	github.com/client9/reopen v1.0.0 // indirect | ||||
|  | @ -69,14 +70,13 @@ require ( | |||
| 	github.com/dlclark/regexp2 v1.4.0 // indirect | ||||
| 	github.com/go-ole/go-ole v1.2.4 // indirect | ||||
| 	github.com/gogo/protobuf v1.3.2 // indirect | ||||
| 	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect | ||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||
| 	github.com/google/go-cmp v0.5.9 // indirect | ||||
| 	github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 // indirect | ||||
| 	github.com/google/pprof v0.0.0-20221102093814-76f304f74e5e // indirect | ||||
| 	github.com/google/uuid v1.3.0 // indirect | ||||
| 	github.com/google/wire v0.5.0 // indirect | ||||
| 	github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.4.0 // indirect | ||||
| 	github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.7.0 // indirect | ||||
| 	github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect | ||||
| 	github.com/hashicorp/yamux v0.1.1 // indirect | ||||
| 	github.com/jmespath/go-jmespath v0.4.0 // indirect | ||||
|  | @ -89,13 +89,13 @@ require ( | |||
| 	github.com/oklog/ulid/v2 v2.0.2 // indirect | ||||
| 	github.com/opentracing/opentracing-go v1.2.0 // indirect | ||||
| 	github.com/philhofer/fwd v1.1.1 // indirect | ||||
| 	github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect | ||||
| 	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/prometheus/client_model v0.3.0 // indirect | ||||
| 	github.com/prometheus/common v0.37.0 // indirect | ||||
| 	github.com/prometheus/procfs v0.8.0 // indirect | ||||
| 	github.com/prometheus/prometheus v0.37.0 // indirect | ||||
| 	github.com/prometheus/prometheus v0.40.5 // indirect | ||||
| 	github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect | ||||
| 	github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect | ||||
| 	github.com/shirou/gopsutil/v3 v3.21.2 // indirect | ||||
|  | @ -105,19 +105,19 @@ require ( | |||
| 	github.com/tklauser/numcpus v0.2.1 // indirect | ||||
| 	github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect | ||||
| 	github.com/uber/jaeger-lib v2.4.1+incompatible // indirect | ||||
| 	go.opencensus.io v0.23.0 // indirect | ||||
| 	go.uber.org/atomic v1.9.0 // indirect | ||||
| 	golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect | ||||
| 	go.opencensus.io v0.24.0 // indirect | ||||
| 	go.uber.org/atomic v1.10.0 // indirect | ||||
| 	golang.org/x/crypto v0.3.0 // indirect | ||||
| 	golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect | ||||
| 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect | ||||
| 	golang.org/x/mod v0.6.0 // indirect | ||||
| 	golang.org/x/sync v0.1.0 // indirect | ||||
| 	golang.org/x/sys v0.2.0 // indirect | ||||
| 	golang.org/x/text v0.4.0 // indirect | ||||
| 	golang.org/x/sys v0.3.0 // indirect | ||||
| 	golang.org/x/text v0.5.0 // indirect | ||||
| 	golang.org/x/time v0.2.0 // indirect | ||||
| 	golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect | ||||
| 	google.golang.org/api v0.91.0 // indirect | ||||
| 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect | ||||
| 	google.golang.org/api v0.103.0 // indirect | ||||
| 	google.golang.org/appengine v1.6.7 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3 // indirect | ||||
| 	gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										756
									
								
								workhorse/go.sum
								
								
								
								
							
							
						
						
									
										756
									
								
								workhorse/go.sum
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -11,6 +11,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" | ||||
| 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container" | ||||
| 	"github.com/BurntSushi/toml" | ||||
| 	"gocloud.dev/blob" | ||||
| 	"gocloud.dev/blob/azureblob" | ||||
|  | @ -178,12 +179,13 @@ func (creds *AzureCredentials) getURLOpener() (*azureblob.URLOpener, error) { | |||
| 		AccountName: creds.AccountName, | ||||
| 	} | ||||
| 
 | ||||
| 	clientFunc := func(svcURL azureblob.ServiceURL) (*azblob.ServiceClient, error) { | ||||
| 	clientFunc := func(svcURL azureblob.ServiceURL, containerName azureblob.ContainerName) (*container.Client, error) { | ||||
| 		sharedKeyCred, err := azblob.NewSharedKeyCredential(creds.AccountName, creds.AccountKey) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("error creating Azure credentials: %w", err) | ||||
| 		} | ||||
| 		return azblob.NewServiceClientWithSharedKey(string(svcURL), sharedKeyCred, &azblob.ClientOptions{}) | ||||
| 		containerURL := fmt.Sprintf("%s/%s", svcURL, containerName) | ||||
| 		return container.NewClientWithSharedKeyCredential(containerURL, sharedKeyCred, &container.ClientOptions{}) | ||||
| 	} | ||||
| 
 | ||||
| 	return &azureblob.URLOpener{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue