Commit 221b5297 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 00a8c64f
<script>
import { DEFAULT_HEADING } from '../constants';
export default {
props: {
title: {
type: String,
required: false,
default: '',
},
},
computed: {
heading() {
return this.title || DEFAULT_HEADING;
},
},
};
</script>
<template>
<div>
<h3 ref="sseHeading">{{ heading }}</h3>
</div>
</template>
......@@ -7,6 +7,11 @@ export default {
GlLoadingIcon,
},
props: {
returnUrl: {
type: String,
required: false,
default: '',
},
saveable: {
type: Boolean,
required: false,
......@@ -23,6 +28,10 @@ export default {
<template>
<div class="d-flex bg-light border-top justify-content-between align-items-center py-3 px-4">
<gl-loading-icon :class="{ invisible: !savingChanges }" size="md" />
<div>
<gl-new-button v-if="returnUrl" ref="returnUrlLink" :href="returnUrl">{{
s__('StaticSiteEditor|Return to site')
}}</gl-new-button>
<gl-new-button
variant="success"
:disabled="!saveable || savingChanges"
......@@ -31,4 +40,5 @@ export default {
{{ __('Submit Changes') }}
</gl-new-button>
</div>
</div>
</template>
......@@ -3,16 +3,25 @@ import { mapState, mapGetters, mapActions } from 'vuex';
import { GlSkeletonLoader } from '@gitlab/ui';
import EditArea from './edit_area.vue';
import EditHeader from './edit_header.vue';
import Toolbar from './publish_toolbar.vue';
export default {
components: {
EditArea,
EditHeader,
GlSkeletonLoader,
Toolbar,
},
computed: {
...mapState(['content', 'isLoadingContent', 'isSavingChanges', 'isContentLoaded']),
...mapState([
'content',
'isLoadingContent',
'isSavingChanges',
'isContentLoaded',
'returnUrl',
'title',
]),
...mapGetters(['contentChanged']),
},
mounted() {
......@@ -36,12 +45,14 @@ export default {
</gl-skeleton-loader>
</div>
<div v-if="isContentLoaded" class="d-flex flex-grow-1 flex-column">
<edit-header class="w-75 align-self-center py-2" :title="title" />
<edit-area
class="w-75 h-100 shadow-none align-self-center"
:value="content"
@input="setContent"
/>
<toolbar
:return-url="returnUrl"
:saveable="contentChanged"
:saving-changes="isSavingChanges"
@submit="submitChanges"
......
......@@ -10,3 +10,5 @@ export const SUBMIT_CHANGES_COMMIT_ERROR = s__(
export const SUBMIT_CHANGES_MERGE_REQUEST_ERROR = s__(
'StaticSiteEditor|Could not create merge request.',
);
export const DEFAULT_HEADING = s__('StaticSiteEditor|Static site editor');
......@@ -3,10 +3,10 @@ import StaticSiteEditor from './components/static_site_editor.vue';
import createStore from './store';
const initStaticSiteEditor = el => {
const { projectId, path: sourcePath } = el.dataset;
const { projectId, returnUrl, path: sourcePath } = el.dataset;
const store = createStore({
initialState: { projectId, sourcePath, username: window.gon.current_username },
initialState: { projectId, returnUrl, sourcePath, username: window.gon.current_username },
});
return new Vue({
......
const createState = (initialState = {}) => ({
username: null,
projectId: null,
returnUrl: null,
sourcePath: null,
isLoadingContent: false,
......
<script>
import { __ } from '~/locale';
import { roundOffFloat } from '~/lib/utils/common_utils';
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -27,6 +28,11 @@ export default {
required: false,
default: 'neutral',
},
unavailableLabel: {
type: String,
required: false,
default: __('Not available'),
},
successCount: {
type: Number,
required: true,
......@@ -103,7 +109,7 @@ export default {
<template>
<div :class="cssClass" class="stacked-progress-bar">
<span v-if="!totalCount" class="status-unavailable"> {{ __('Not available') }} </span>
<span v-if="!totalCount" class="status-unavailable">{{ unavailableLabel }}</span>
<span
v-if="successPercent"
v-tooltip
......
......@@ -9,7 +9,7 @@ module Groups
respond_to do |format|
format.html
format.json do
@images = group.container_repositories.with_api_entity_associations
@images = ContainerRepositoriesFinder.new(user: current_user, subject: group).execute.with_api_entity_associations
track_event(:list_repositories)
......
......@@ -2,10 +2,13 @@
class Projects::StaticSiteEditorController < Projects::ApplicationController
include ExtractsPath
include CreatesCommit
layout 'fullscreen'
prepend_before_action :authenticate_user!, only: [:show]
before_action :assign_ref_and_path, only: [:show]
before_action :authorize_edit_tree!, only: [:show]
def show
@config = Gitlab::StaticSiteEditor::Config.new(@repository, @ref, @path, params[:return_url])
......
......@@ -14,6 +14,7 @@
# active: boolean
# blocked: boolean
# external: boolean
# without_projects: boolean
#
class UsersFinder
include CreatedAtFilter
......@@ -36,6 +37,7 @@ class UsersFinder
users = by_external(users)
users = by_2fa(users)
users = by_created_at(users)
users = by_without_projects(users)
users = by_custom_attributes(users)
users
......@@ -94,6 +96,12 @@ class UsersFinder
users
end
end
def by_without_projects(users)
return users unless params[:without_projects]
users.without_projects
end
end
UsersFinder.prepend_if_ee('EE::UsersFinder')
---
title: Update aws-ecs image location in CI template
merge_request: 27382
author:
type: changed
---
title: Group level container registry show subgroups repos
merge_request: 29263
author:
type: fixed
---
title: Add API endpoint to get users without projects
merge_request: 29347
author:
type: added
---
title: Add Gitlab User-Agent to ContainerRegistry::Client
merge_request: 29294
author: Sashi Kumar
type: other
......@@ -397,9 +397,9 @@ CREATE TABLE public.application_settings (
email_restrictions text,
npm_package_requests_forwarding boolean DEFAULT true NOT NULL,
namespace_storage_size_limit bigint DEFAULT 0 NOT NULL,
issues_create_limit integer DEFAULT 300 NOT NULL,
seat_link_enabled boolean DEFAULT true NOT NULL,
container_expiration_policies_enable_historic_entries boolean DEFAULT false NOT NULL
container_expiration_policies_enable_historic_entries boolean DEFAULT false NOT NULL,
issues_create_limit integer DEFAULT 300 NOT NULL
);
CREATE SEQUENCE public.application_settings_id_seq
......
......@@ -353,7 +353,7 @@ you can flip the feature flag from a Rails console.
```shell
cd /home/git/gitlab
RAILS_ENV=production sudo -u git -H bundle exec rails console
sudo -u git -H bundle exec rails console -e production
```
1. Flip the switch and disable it:
......
......@@ -106,7 +106,7 @@ gitlab-rails console
# Installation from source
cd /home/git/gitlab
sudo -u git -H bin/rails console RAILS_ENV=production
sudo -u git -H bin/rails console -e production
```
**To check if incremental logging (trace) is enabled:**
......
......@@ -30,7 +30,7 @@ sudo gitlab-rails console
For source installations, you'll have to instead run:
```shell
sudo -u git -H bundle exec rails console RAILS_ENV=production
sudo -u git -H bundle exec rails console -e production
```
Further code examples will all take place inside the Rails console and also
......
......@@ -2159,6 +2159,11 @@ type Epic implements Noteable {
"""
hasIssues: Boolean!
"""
Indicates if the epic has a parent epic
"""
hasParent: Boolean!
"""
Current health status of the epic
"""
......
......@@ -6336,6 +6336,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasParent",
"description": "Indicates if the epic has a parent epic",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "healthStatus",
"description": "Current health status of the epic",
......
......@@ -359,6 +359,7 @@ Represents an epic.
| `group` | Group! | Group to which the epic belongs |
| `hasChildren` | Boolean! | Indicates if the epic has children |
| `hasIssues` | Boolean! | Indicates if the epic has direct issues |
| `hasParent` | Boolean! | Indicates if the epic has a parent epic |
| `healthStatus` | EpicHealthStatus | Current health status of the epic |
| `id` | ID! | ID of the epic |
| `iid` | ID! | Internal ID of the epic |
......
......@@ -75,6 +75,7 @@ GET /users
| `order_by` | string | no | Return users ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
| `sort` | string | no | Return users sorted in `asc` or `desc` order. Default is `desc` |
| `two_factor` | string | no | Filter users by Two-factor authentication. Filter values are `enabled` or `disabled`. By default it returns all users |
| `without_projects` | boolean | no | Filter users without projects. Default is `false` |
```json
[
......@@ -207,6 +208,8 @@ You can search users by creation date time range with:
GET /users?created_before=2001-01-02T00:00:00.060Z&created_after=1999-01-02T00:00:00.060
```
You can search for users without projects with: `/users?without_projects=true`
You can filter by [custom attributes](custom_attributes.md) with:
```plaintext
......
......@@ -39,7 +39,7 @@ Some credentials are required to be able to run `aws` commands:
```yml
deploy:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy:latest # see the note below
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest # see the note below
script:
- aws s3 ...
- aws create-deployment ...
......@@ -47,7 +47,7 @@ Some credentials are required to be able to run `aws` commands:
NOTE: **Note:**
Please note that the image used in the example above
(`registry.gitlab.com/gitlab-org/cloud-deploy:latest`) is hosted on the [GitLab
(`registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest`) is hosted on the [GitLab
Container Registry](../../user/packages/container_registry/index.md) and is
ready to use. Alternatively, replace the image with another one hosted on [AWS ECR](#aws-ecr).
......@@ -119,3 +119,15 @@ After you're all set up on AWS ECS, follow these steps:
Finally, your AWS ECS service will be updated with the new revision of the
task definition, making the cluster pull the newest version of your
application.
Alternatively, if you don't wish to use the `Deploy-ECS.gitlab-ci.yml` template
to deploy to AWS ECS, you can always use our
`aws-base` Docker image to run your own [AWS CLI commands for ECS](https://docs.aws.amazon.com/cli/latest/reference/ecs/index.html#cli-aws-ecs).
```yaml
deploy:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
script:
- aws ecs register-task-definition ...
```
......@@ -41,8 +41,8 @@ Access the database via one of these commands (they all get you to the same plac
```ruby
gdk psql -d gitlabhq_development
bundle exec rails dbconsole RAILS_ENV=development
bundle exec rails db RAILS_ENV=development
bundle exec rails dbconsole -e development
bundle exec rails db -e development
```
- `\q`: Quit/exit
......
......@@ -78,7 +78,7 @@ The last option is to import a project using a Rails console:
gitlab-rails console
# For installations from source
sudo -u git -H bundle exec rails console RAILS_ENV=production
sudo -u git -H bundle exec rails console -e production
```
1. Create a project and run `Project::TreeRestorer`:
......
......@@ -136,28 +136,18 @@ browser performance testing using a
### Node pools
Both `review-apps-ce` and `review-apps-ee` clusters are currently set up with
two node pools:
The `review-apps-ee` and `review-apps-ce` clusters are currently set up with
the following node pools:
- a node pool of non-preemptible `n1-standard-2` (2 vCPU, 7.5 GB memory) nodes
dedicated to the `tiller` deployment (see below) with a single node.
- a node pool of preemptible `n1-standard-2` (2 vCPU, 7.5 GB memory) nodes,
with a minimum of 1 node and a maximum of 250 nodes.
- `review-apps-ee` of preemptible `e2-highcpu-16` (16 vCPU, 16 GB memory) nodes with autoscaling
- `review-apps-ce` of preemptible `n1-standard-8` (8 vCPU, 16 GB memory) nodes with autoscaling
### Helm/Tiller
### Helm
The Helm/Tiller version used is defined in the
[`registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base` image](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/Dockerfile.gitlab-charts-build-base#L4)
The Helm version used is defined in the
[`registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-helm3-kubectl1.14` image](https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/Dockerfile.gitlab-helm3-kubectl1.14#L7)
used by the `review-deploy` and `review-stop` jobs.
The `tiller` deployment (the Helm server) is deployed to a dedicated node pool
that has the `app=helm` label and a specific
[taint](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
to prevent other pods from being scheduled on this node pool.
This is to ensure Tiller isn't affected by "noisy" neighbors that could put
their node under pressure.
## How to
### Get access to the GCP Review Apps cluster
......@@ -241,7 +231,7 @@ due to Helm or Kubernetes trying to recreate the components.
**Where to look for further debugging:**
Look at a recent `review-deploy` job log, and at the Tiller logs.
Look at a recent `review-deploy` job log.
**Useful commands:**
......
......@@ -42,7 +42,7 @@ gitlab-rails console
# Installation from source
cd /home/git/gitlab
sudo -u git -H bin/rails console RAILS_ENV=production
sudo -u git -H bin/rails console -e production
```
Then run the following command to enable the feature flag:
......
......@@ -947,7 +947,7 @@ backup beforehand.
For installations from source:
```shell
sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production
sudo -u git -H bundle exec rails dbconsole -e production
```
1. Check the `ci_group_variables` and `ci_variables` tables:
......@@ -982,7 +982,7 @@ backup beforehand.
For installations from source:
```shell
sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production
sudo -u git -H bundle exec rails dbconsole -e production
```
1. Clear all the tokens for projects, groups, and the whole instance:
......@@ -1015,7 +1015,7 @@ backup beforehand.
For installations from source:
```shell
sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production
sudo -u git -H bundle exec rails dbconsole -e production
```
1. Clear all the tokens for pending jobs:
......
......@@ -16,7 +16,7 @@ To unlock a locked user:
sudo gitlab-rails console -e production
## For installations from source
sudo -u git -H bundle exec rails console RAILS_ENV=production
sudo -u git -H bundle exec rails console -e production
```
1. Find the user to unlock. You can search by email or ID.
......
......@@ -277,7 +277,7 @@ gitlab-rails console
# Installation from source
cd /home/git/gitlab
sudo -u git -H bin/rails console RAILS_ENV=production
sudo -u git -H bin/rails console -e production
```
Then run `Feature.enable(:approval_rules)` to enable the updated interface.
......
......@@ -82,6 +82,7 @@ module API
optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
optional :created_after, type: DateTime, desc: 'Return users created after the specified time'
optional :created_before, type: DateTime, desc: 'Return users created before the specified time'
optional :without_projects, type: Boolean, default: false, desc: 'Filters only users without projects'
all_or_none_of :extern_uid, :provider
use :sort_params
......@@ -94,7 +95,7 @@ module API
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
unless current_user&.admin?
params.except!(:created_after, :created_before, :order_by, :sort, :two_factor)
params.except!(:created_after, :created_before, :order_by, :sort, :two_factor, :without_projects)
end
users = UsersFinder.new(current_user, params).execute
......
......@@ -159,13 +159,13 @@ module ContainerRegistry
end
def faraday
@faraday ||= Faraday.new(@base_uri) do |conn|
@faraday ||= faraday_base do |conn|
initialize_connection(conn, @options, &method(:accept_manifest))
end
end
def faraday_blob
@faraday_blob ||= Faraday.new(@base_uri) do |conn|
@faraday_blob ||= faraday_base do |conn|
initialize_connection(conn, @options)
end
end
......@@ -173,12 +173,16 @@ module ContainerRegistry
# Create a new request to make sure the Authorization header is not inserted
# via the Faraday middleware
def faraday_redirect
@faraday_redirect ||= Faraday.new(@base_uri) do |conn|
@faraday_redirect ||= faraday_base do |conn|
conn.request :json
conn.adapter :net_http
end
end
def faraday_base(&block)
Faraday.new(@base_uri, headers: { user_agent: "GitLab/#{Gitlab::VERSION}" }, &block)
end
def delete_if_exists(path)
result = faraday.delete(path)
......
......@@ -9,7 +9,7 @@ include:
- template: Jobs/Build.gitlab-ci.yml
.deploy_to_ecs:
image: registry.gitlab.com/gitlab-org/cloud-deploy:latest
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest
script:
- ecs update-task-definition
......
......@@ -3,33 +3,49 @@
module Gitlab
module StaticSiteEditor
class Config
SUPPORTED_EXTENSIONS = %w[.md].freeze
def initialize(repository, ref, file_path, return_url)
@repository = repository
@ref = ref
@file_path = file_path
@return_url = return_url
@commit_id = repository.commit(ref)&.id if ref
end
def payload
{
branch: ref,
path: file_path,
commit: commit.id,
commit_id: commit_id,
project_id: project.id,
project: project.path,
namespace: project.namespace.path,
return_url: return_url
return_url: return_url,
is_supported_content: supported_content?
}
end
private
attr_reader :repository, :ref, :file_path, :return_url
attr_reader :repository, :ref, :file_path, :return_url, :commit_id
delegate :project, to: :repository
def commit
repository.commit(ref)
def supported_content?
master_branch? && extension_supported? && file_exists?
end
def master_branch?
ref == 'master'
end
def extension_supported?
File.extname(file_path).in?(SUPPORTED_EXTENSIONS)
end
def file_exists?
commit_id.present? && repository.blob_at(commit_id, file_path).present?
end
end
end
......
......@@ -11891,6 +11891,9 @@ msgstr ""
msgid "Learn how to %{no_packages_link_start}publish and share your packages%{no_packages_link_end} with GitLab."
msgstr ""
msgid "Learn how to enable synchronization"
msgstr ""
msgid "Learn more"
msgstr ""
......@@ -13827,6 +13830,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
msgid "Nothing to synchronize"
msgstr ""
msgid "Notification events"
msgstr ""
......@@ -19452,6 +19458,9 @@ msgstr ""
msgid "StaticSiteEditor|Return to site"
msgstr ""
msgid "StaticSiteEditor|Static site editor"
msgstr ""
msgid "StaticSiteEditor|Success!"
msgstr ""
......@@ -19866,6 +19875,12 @@ msgstr ""
msgid "Synced"
msgstr ""
msgid "Synchronization disabled"
msgstr ""
msgid "Synchronization of container repositories is disabled."
msgstr ""
msgid "System"
msgstr ""
......
......@@ -7,6 +7,13 @@ describe Groups::Registry::RepositoriesController do
let_it_be(:guest) { create(:user) }
let_it_be(:group, reload: true) { create(:group) }
subject do
get :index, params: {
group_id: group,
format: format
}
end
before do
stub_container_registry_config(enabled: true)
group.add_owner(user)
......@@ -15,51 +22,67 @@ describe Groups::Registry::RepositoriesController do
end
shared_examples 'renders a list of repositories' do
let_it_be(:repo) { create_project_with_repo(test_group) }
it 'returns a list of projects for json format' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_kind_of(Array)
expect(json_response.first).to include(
'id' => repo.id,
'name' => repo.name
)
end
end
shared_examples 'renders correctly' do
context 'when user has access to registry' do
let_it_be(:test_group) { group }
context 'html format' do
let(:format) { :html }
it 'show index page' do
expect(Gitlab::Tracking).not_to receive(:event)
get :index, params: {
group_id: group
}
subject
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'json format' do
let(:format) { :json }
it 'has the correct response schema' do
get :index, params: {
group_id: group,
format: :json
}
subject
expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers
end
it 'returns a list of projects for json format' do
project = create(:project, group: group)
repo = create(:container_repository, project: project)
it_behaves_like 'renders a list of repositories'
get :index, params: {
group_id: group,
format: :json
}
it_behaves_like 'a gitlab tracking event', described_class.name, 'list_repositories'
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_kind_of(Array)
expect(json_response.first).to include(
'id' => repo.id,
'name' => repo.name
)
end
context 'with project in subgroup' do
let_it_be(:test_group) { create(:group, parent: group ) }
it 'tracks the event' do
expect(Gitlab::Tracking).to receive(:event).with(anything, 'list_repositories', {})
it_behaves_like 'renders a list of repositories'
get :index, params: {
group_id: group,
format: :json
}
context 'with project in subgroup and group' do
let_it_be(:repo_in_test_group) { create_project_with_repo(test_group) }
let_it_be(:repo_in_group) { create_project_with_repo(group) }
it 'returns all the projects' do
subject
expect(json_response).to be_kind_of(Array)
expect(json_response.length).to eq 2
end
end
end
end
end
......@@ -69,20 +92,30 @@ describe Groups::Registry::RepositoriesController do
sign_in(guest)
end
it 'renders not found' do
get :index, params: {
group_id: group
}
expect(response).to have_gitlab_http_status(:not_found)
context 'json format' do
let(:format) { :json }
it_behaves_like 'returning response status', :not_found
end
context 'html format' do
let(:format) { :html }
it_behaves_like 'returning response status', :not_found
end
end
end
context 'GET #index' do
it_behaves_like 'renders a list of repositories'
it_behaves_like 'renders correctly'
end
context 'GET #show' do
it_behaves_like 'renders a list of repositories'
it_behaves_like 'renders correctly'
end
def create_project_with_repo(group)
project = create(:project, group: test_group)
create(:container_repository, project: project)
end
end
......@@ -26,7 +26,21 @@ describe Projects::StaticSiteEditorController do
end
end
%w[guest developer maintainer].each do |role|
context 'as guest' do
let(:user) { create(:user) }
before do
project.add_guest(user)
sign_in(user)
get :show, params: default_params
end
it 'responds with 404 page' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
%w[developer maintainer].each do |role|
context "as #{role}" do
let(:user) { create(:user) }
......
import { shallowMount } from '@vue/test-utils';
import EditHeader from '~/static_site_editor/components/edit_header.vue';
import { DEFAULT_HEADING } from '~/static_site_editor/constants';
import { sourceContentTitle } from '../mock_data';
describe('~/static_site_editor/components/edit_header.vue', () => {
let wrapper;
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(EditHeader, {
propsData: {
...propsData,
},
});
};
const findHeading = () => wrapper.find({ ref: 'sseHeading' });
beforeEach(() => {
buildWrapper();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the default heading if there is no title prop', () => {
expect(findHeading().text()).toBe(DEFAULT_HEADING);
});
it('renders the title prop value in the heading', () => {
buildWrapper({ title: sourceContentTitle });
expect(findHeading().text()).toBe(sourceContentTitle);
});
});
......@@ -3,6 +3,8 @@ import { GlNewButton, GlLoadingIcon } from '@gitlab/ui';
import PublishToolbar from '~/static_site_editor/components/publish_toolbar.vue';
import { returnUrl } from '../mock_data';
describe('Static Site Editor Toolbar', () => {
let wrapper;
......@@ -15,6 +17,7 @@ describe('Static Site Editor Toolbar', () => {
});
};
const findReturnUrlLink = () => wrapper.find({ ref: 'returnUrlLink' });
const findSaveChangesButton = () => wrapper.find(GlNewButton);
const findLoadingIndicator = () => wrapper.find(GlLoadingIcon);
......@@ -38,6 +41,17 @@ describe('Static Site Editor Toolbar', () => {
expect(findLoadingIndicator().classes()).toContain('invisible');
});
it('does not render returnUrl link', () => {
expect(findReturnUrlLink().exists()).toBe(false);
});
it('renders returnUrl link when returnUrl prop exists', () => {
buildWrapper({ returnUrl });
expect(findReturnUrlLink().exists()).toBe(true);
expect(findReturnUrlLink().attributes('href')).toBe(returnUrl);
});
describe('when saveable', () => {
it('enables Submit Changes button', () => {
buildWrapper({ saveable: true });
......
......@@ -7,9 +7,10 @@ import createState from '~/static_site_editor/store/state';
import StaticSiteEditor from '~/static_site_editor/components/static_site_editor.vue';
import EditArea from '~/static_site_editor/components/edit_area.vue';
import EditHeader from '~/static_site_editor/components/edit_header.vue';
import PublishToolbar from '~/static_site_editor/components/publish_toolbar.vue';
import { sourceContent } from '../mock_data';
import { sourceContent, sourceContentTitle } from '../mock_data';
const localVue = createLocalVue();
......@@ -60,6 +61,7 @@ describe('StaticSiteEditor', () => {
};
const findEditArea = () => wrapper.find(EditArea);
const findEditHeader = () => wrapper.find(EditHeader);
const findPublishToolbar = () => wrapper.find(PublishToolbar);
const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader);
......@@ -77,16 +79,21 @@ describe('StaticSiteEditor', () => {
expect(findEditArea().exists()).toBe(false);
});
it('does not render edit header', () => {
expect(findEditHeader().exists()).toBe(false);
});
it('does not render toolbar', () => {
expect(findPublishToolbar().exists()).toBe(false);
});
});
describe('when content is loaded', () => {
const content = 'edit area content';
const content = sourceContent;
const title = sourceContentTitle;
beforeEach(() => {
buildContentLoadedStore({ initialState: { content } });
buildContentLoadedStore({ initialState: { content, title } });
buildWrapper();
});
......@@ -94,6 +101,10 @@ describe('StaticSiteEditor', () => {
expect(findEditArea().exists()).toBe(true);
});
it('renders the edit header', () => {
expect(findEditHeader().exists()).toBe(true);
});
it('does not render skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(false);
});
......@@ -102,6 +113,10 @@ describe('StaticSiteEditor', () => {
expect(findEditArea().props('value')).toBe(content);
});
it('passes page title to edit header', () => {
expect(findEditHeader().props('title')).toBe(title);
});
it('renders toolbar', () => {
expect(findPublishToolbar().exists()).toBe(true);
});
......
......@@ -11,11 +11,11 @@ twitter_image: '/images/tweets/handbook-gitlab.png'
- TOC
{:toc .hidden-md .hidden-lg}
`;
export const sourceContentTitle = 'Handbook';
export const username = 'gitlabuser';
export const projectId = '123456';
export const returnUrl = 'https://www.gitlab.com';
export const sourcePath = 'foobar.md.html';
export const savedContentMeta = {
......
......@@ -6,6 +6,21 @@ describe ContainerRegistry::Client do
let(:token) { '12345' }
let(:options) { { token: token } }
let(:client) { described_class.new("http://container-registry", options) }
let(:push_blob_headers) do
{
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
'Authorization' => "bearer #{token}",
'Content-Type' => 'application/octet-stream',
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
}
end
let(:headers_with_accept_types) do
{
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
'Authorization' => "bearer #{token}",
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
}
end
shared_examples '#repository_manifest' do |manifest_type|
let(:manifest) do
......@@ -31,8 +46,9 @@ describe ContainerRegistry::Client do
it 'GET /v2/:name/manifests/mytag' do
stub_request(:get, "http://container-registry/v2/group/test/manifests/mytag")
.with(headers: {
'Accept' => described_class::ACCEPTED_TYPES.join(', '),
'Authorization' => "bearer #{token}"
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
'Authorization' => "bearer #{token}",
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
})
.to_return(status: 200, body: manifest.to_json, headers: { content_type: manifest_type })
......@@ -44,12 +60,23 @@ describe ContainerRegistry::Client do
it_behaves_like '#repository_manifest', described_class::OCI_MANIFEST_V1_TYPE
describe '#blob' do
let(:blob_headers) do
{
'Accept' => 'application/octet-stream',
'Authorization' => "bearer #{token}",
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
}
end
let(:redirect_header) do
{
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
}
end
it 'GET /v2/:name/blobs/:digest' do
stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
.with(headers: {
'Accept' => 'application/octet-stream',
'Authorization' => "bearer #{token}"
})
.with(headers: blob_headers)
.to_return(status: 200, body: "Blob")
expect(client.blob('group/test', 'sha256:0123456789012345')).to eq('Blob')
......@@ -57,15 +84,14 @@ describe ContainerRegistry::Client do
it 'follows 307 redirect for GET /v2/:name/blobs/:digest' do
stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
.with(headers: {
'Accept' => 'application/octet-stream',
'Authorization' => "bearer #{token}"
})
.with(headers: blob_headers)
.to_return(status: 307, body: "", headers: { Location: 'http://redirected' })
# We should probably use hash_excluding here, but that requires an update to WebMock:
# https://github.com/bblimke/webmock/blob/master/lib/webmock/matchers/hash_excluding_matcher.rb
stub_request(:get, "http://redirected/")
.with { |request| !request.headers.include?('Authorization') }
.with(headers: redirect_header) do |request|
!request.headers.include?('Authorization')
end
.to_return(status: 200, body: "Successfully redirected")
response = client.blob('group/test', 'sha256:0123456789012345')
......@@ -76,10 +102,11 @@ describe ContainerRegistry::Client do
def stub_upload(path, content, digest, status = 200)
stub_request(:post, "http://container-registry/v2/#{path}/blobs/uploads/")
.with(headers: headers_with_accept_types)
.to_return(status: status, body: "", headers: { 'location' => 'http://container-registry/next_upload?id=someid' })
stub_request(:put, "http://container-registry/next_upload?digest=#{digest}&id=someid")
.with(body: content)
.with(body: content, headers: push_blob_headers)
.to_return(status: status, body: "", headers: {})
end
......@@ -136,11 +163,20 @@ describe ContainerRegistry::Client do
end
describe '#put_tag' do
let(:manifest_headers) do
{
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
'Authorization' => "bearer #{token}",
'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json',
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
}
end
subject { client.put_tag('path', 'tagA', { foo: :bar }) }
it 'uploads the manifest and returns the digest' do
stub_request(:put, "http://container-registry/v2/path/manifests/tagA")
.with(body: "{\n \"foo\": \"bar\"\n}")
.with(body: "{\n \"foo\": \"bar\"\n}", headers: manifest_headers)
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:123' })
expect(subject).to eq 'sha256:123'
......@@ -153,6 +189,7 @@ describe ContainerRegistry::Client do
context 'when the tag exists' do
before do
stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a")
.with(headers: headers_with_accept_types)
.to_return(status: 200, body: "")
end
......@@ -162,6 +199,7 @@ describe ContainerRegistry::Client do
context 'when the tag does not exist' do
before do
stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a")
.with(headers: headers_with_accept_types)
.to_return(status: 404, body: "")
end
......@@ -171,6 +209,7 @@ describe ContainerRegistry::Client do
context 'when an error occurs' do
before do
stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a")
.with(headers: headers_with_accept_types)
.to_return(status: 500, body: "")
end
......
......@@ -18,13 +18,44 @@ describe Gitlab::StaticSiteEditor::Config do
it 'returns data for the frontend component' do
is_expected.to eq(
branch: 'master',
commit: repository.commit.id,
commit_id: repository.commit.id,
namespace: 'namespace',
path: 'README.md',
project: 'project',
project_id: project.id,
return_url: 'http://example.com'
return_url: 'http://example.com',
is_supported_content: true
)
end
context 'when branch is not master' do
let(:ref) { 'my-branch' }
it { is_expected.to include(is_supported_content: false) }
end
context 'when file does not have a markdown extension' do
let(:file_path) { 'README.txt' }
it { is_expected.to include(is_supported_content: false) }
end
context 'when file does not have an extension' do
let(:file_path) { 'README' }
it { is_expected.to include(is_supported_content: false) }
end
context 'when file does not exist' do
let(:file_path) { 'UNKNOWN.md' }
it { is_expected.to include(is_supported_content: false) }
end
context 'when repository is empty' do
let(:project) { create(:project_empty_repo) }
it { is_expected.to include(is_supported_content: false) }
end
end
end
......@@ -280,6 +280,18 @@ describe API::Users, :do_not_mock_admin_mode do
expect(json_response.first['id']).to eq(user_with_2fa.id)
end
it "returns users without projects" do
user_without_projects = create(:user)
create(:project, namespace: user.namespace)
create(:project, namespace: admin.namespace)
get api('/users', admin), params: { without_projects: true }
expect(response).to match_response_schema('public_api/v4/user/admins')
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(user_without_projects.id)
end
it 'returns 400 when provided incorrect sort params' do
get api('/users', admin), params: { order_by: 'magic', sort: 'asc' }
......
# frozen_string_literal: true
RSpec.shared_examples 'returning response status' do |status|
it "returns #{status}" do
subject
expect(response).to have_gitlab_http_status(status)
end
end
......@@ -596,22 +596,6 @@ describe ObjectStorage do
uploader.cache!(uploaded_file)
end
context 'when local file is used' do
context 'when valid file is used' do
let(:uploaded_file) do
fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
end
it "properly caches the file" do
subject
expect(uploader).to be_exists
expect(uploader.path).to start_with(uploader_class.root)
expect(uploader.filename).to eq('rails_sample.jpg')
end
end
end
context 'when local file is used' do
let(:temp_file) { Tempfile.new("test") }
......@@ -627,6 +611,14 @@ describe ObjectStorage do
context 'when valid file is specified' do
let(:uploaded_file) { temp_file }
it 'properly caches the file' do
subject
expect(uploader).to be_exists
expect(uploader.path).to start_with(uploader_class.root)
expect(uploader.filename).to eq(File.basename(uploaded_file.path))
end
context 'when object storage and direct upload is specified' do
before do
stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: true)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment