Commit 25cc1dde authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 46971-extra-whitespace

* master: (32 commits)
  Update CHANGELOG.md for 10.8.3
  Update "col-xs" usage to bootstrap4
  Add missing usage_ping_enabled to API settings
  Make Repository#blob_data_at a public method
  Fix UI broken in line profiling modal due to Bootstrap 4
  Only preload member records for the relevant projects/groups/user in projects API
  Fix   after sign-in with Google button
  Fix a wrong link reference in docs
  Simplify spec: `2.times.each` -> `2.times`
  Replace grape-route-helpers with our own grape-path-helpers
  Add archived param to the json response
  Consider we might have prefix for stable branches
  Convert spec/javascripts/.eslintrc to yml format as well
  Remove version lock on awesome_print dependency
  Fix import project by url layout
  Fix missing timeout value in object storage pre-authorization call
  fix last lints
  disable no-multi-assign
  fix `spaced-comment`
  remove unused createComponentWithMixin from vue spec helpers
  ...
parents bcf30db8 37a7b590
{
"env": {
"browser": true,
"es6": true
},
"extends": [
"airbnb-base",
"plugin:vue/recommended"
],
"globals": {
"__webpack_public_path__": true,
"gl": false,
"gon": false,
"localStorage": false
},
"parserOptions": {
"parser": "babel-eslint"
},
"plugins": [
"filenames",
"import",
"html",
"promise"
],
"settings": {
"html/html-extensions": [".html", ".html.raw"],
"import/resolver": {
"webpack": {
"config": "./config/webpack.config.js"
}
}
},
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+$"],
"import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error",
"no-underscore-dangle": ["error", { "allow": ["__", "_links"] }],
"no-mixed-operators": 0,
"space-before-function-paren": 0,
"curly": 0,
"arrow-parens": 0,
"vue/html-self-closing": [
"error",
{
"html": {
"void": "always",
"normal": "never",
"component": "always"
},
"svg": "always",
"math": "always"
}
]
}
}
---
env:
browser: true
es6: true
extends:
- airbnb-base
- plugin:vue/recommended
globals:
__webpack_public_path__: true
gl: false
gon: false
localStorage: false
parserOptions:
parser: babel-eslint
plugins:
- filenames
- import
- html
- promise
settings:
html/html-extensions:
- ".html"
- ".html.raw"
import/resolver:
webpack:
config: "./config/webpack.config.js"
rules:
filenames/match-regex:
- error
- "^[a-z0-9_]+$"
import/no-commonjs: error
no-multiple-empty-lines:
- error
- max: 1
promise/catch-or-return: error
no-underscore-dangle:
- error
- allow:
- __
- _links
no-mixed-operators: off
vue/html-self-closing:
- error
- html:
void: always
normal: never
component: always
svg: always
math: always
## Conflicting rules with prettier:
space-before-function-paren: off
curly: off
arrow-parens: off
function-paren-newline: off
object-curly-newline: off
padded-blocks: off
# Disabled for now, to make the eslint 3 -> eslint 4 update smoother
## Indent rule. We are using the old for now: https://eslint.org/docs/user-guide/migrating-to-4.0.0#indent-rewrite
indent: off
indent-legacy:
- error
- 2
- SwitchCase: 1
VariableDeclarator: 1
outerIIFEBody: 1
FunctionDeclaration:
parameters: 1
body: 1
FunctionExpression:
parameters: 1
body: 1
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
prefer-destructuring: off
## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
no-restricted-globals: off
## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
no-multi-assign: off
...@@ -591,7 +591,7 @@ ee_compat_check: ...@@ -591,7 +591,7 @@ ee_compat_check:
except: except:
- master - master
- tags - tags
- /^[\d-]+-stable(-ee)?/ - /[\d-]+-stable(-ee)?/
- /^security-/ - /^security-/
- branches@gitlab-org/gitlab-ee - branches@gitlab-org/gitlab-ee
- branches@gitlab/gitlab-ee - branches@gitlab/gitlab-ee
......
...@@ -2,6 +2,20 @@ ...@@ -2,6 +2,20 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.8.3 (2018-05-30)
### Fixed (4 changes)
- Replace Gitlab::REVISION with Gitlab.revision and handle installations without a .git directory. !19125
- Fix encoding of branch names on compare and new merge request page. !19143
- Fix remote mirror database inconsistencies when upgrading from EE to CE. !19196
- Fix local storage not being cleared after creating a new issue.
### Performance (1 change)
- Memoize Gitlab::Database.version.
## 10.8.2 (2018-05-28) ## 10.8.2 (2018-05-28)
### Security (3 changes) ### Security (3 changes)
......
...@@ -28,7 +28,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql ...@@ -28,7 +28,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.27' gem 'rugged', '~> 0.27'
gem 'grape-route-helpers', '~> 2.1.0' gem 'grape-path-helpers', '~> 1.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
...@@ -219,7 +219,7 @@ gem 'asana', '~> 0.6.0' ...@@ -219,7 +219,7 @@ gem 'asana', '~> 0.6.0'
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# Kubernetes integration # Kubernetes integration
gem 'kubeclient', '~> 3.0' gem 'kubeclient', '~> 3.1.0'
# Sanitize user input # Sanitize user input
gem 'sanitize', '~> 2.0' gem 'sanitize', '~> 2.0'
...@@ -320,7 +320,7 @@ group :development, :test do ...@@ -320,7 +320,7 @@ group :development, :test do
gem 'pry-byebug', '~> 3.4.1', platform: :mri gem 'pry-byebug', '~> 3.4.1', platform: :mri
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.8.0', require: false gem 'awesome_print', require: false
gem 'fuubar', '~> 2.2.0' gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.5.0' gem 'database_cleaner', '~> 1.5.0'
......
...@@ -168,7 +168,7 @@ GEM ...@@ -168,7 +168,7 @@ GEM
diff-lcs (1.3) diff-lcs (1.3)
diffy (3.1.0) diffy (3.1.0)
docile (1.1.5) docile (1.1.5)
domain_name (0.5.20170404) domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.2) doorkeeper (4.3.2)
railties (>= 4.2) railties (>= 4.2)
...@@ -348,7 +348,7 @@ GEM ...@@ -348,7 +348,7 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (1.0.2) grape (1.0.3)
activesupport activesupport
builder builder
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
...@@ -358,10 +358,10 @@ GEM ...@@ -358,10 +358,10 @@ GEM
grape-entity (0.7.1) grape-entity (0.7.1)
activesupport (>= 4.0) activesupport (>= 4.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-route-helpers (2.1.0) grape-path-helpers (1.0.1)
activesupport activesupport (~> 4)
grape (>= 0.16.0) grape (~> 1.0)
rake rake (~> 12)
grape_logging (1.7.0) grape_logging (1.7.0)
grape grape
grpc (1.11.0) grpc (1.11.0)
...@@ -446,9 +446,9 @@ GEM ...@@ -446,9 +446,9 @@ GEM
knapsack (1.16.0) knapsack (1.16.0)
rake rake
timecop (>= 0.1.0) timecop (>= 0.1.0)
kubeclient (3.0.0) kubeclient (3.1.0)
http (~> 2.2.2) http (~> 2.2.2)
recursive-open-struct (~> 1.0.4) recursive-open-struct (~> 1.0, >= 1.0.4)
rest-client (~> 2.0) rest-client (~> 2.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
...@@ -698,7 +698,7 @@ GEM ...@@ -698,7 +698,7 @@ GEM
re2 (1.1.1) re2 (1.1.1)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
recursive-open-struct (1.0.5) recursive-open-struct (1.1.0)
redcarpet (3.4.0) redcarpet (3.4.0)
redis (3.3.5) redis (3.3.5)
redis-actionpack (5.0.2) redis-actionpack (5.0.2)
...@@ -977,7 +977,7 @@ DEPENDENCIES ...@@ -977,7 +977,7 @@ DEPENDENCIES
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4) asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print (~> 1.8.0) awesome_print
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
batch-loader (~> 1.2.1) batch-loader (~> 1.2.1)
...@@ -1049,7 +1049,7 @@ DEPENDENCIES ...@@ -1049,7 +1049,7 @@ DEPENDENCIES
gpgme gpgme
grape (~> 1.0) grape (~> 1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-route-helpers (~> 2.1.0) grape-path-helpers (~> 1.0)
grape_logging (~> 1.7) grape_logging (~> 1.7)
grpc (~> 1.11.0) grpc (~> 1.11.0)
haml_lint (~> 0.26.0) haml_lint (~> 0.26.0)
...@@ -1067,7 +1067,7 @@ DEPENDENCIES ...@@ -1067,7 +1067,7 @@ DEPENDENCIES
jwt (~> 1.5.6) jwt (~> 1.5.6)
kaminari (~> 1.0) kaminari (~> 1.0)
knapsack (~> 1.16) knapsack (~> 1.16)
kubeclient (~> 3.0) kubeclient (~> 3.1.0)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder (~> 3.1) license_finder (~> 3.1)
licensee (~> 8.9) licensee (~> 8.9)
......
...@@ -41,10 +41,10 @@ gl.issueBoards.ModalEmptyState = Vue.extend({ ...@@ -41,10 +41,10 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
template: ` template: `
<section class="empty-state"> <section class="empty-state">
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-6 order-sm-last"> <div class="col-12 col-md-6 order-md-last">
<aside class="svg-content"><img :src="emptyStateSvg"/></aside> <aside class="svg-content"><img :src="emptyStateSvg"/></aside>
</div> </div>
<div class="col-xs-12 col-sm-6 order-sm-first"> <div class="col-12 col-md-6 order-md-first">
<div class="text-content"> <div class="text-content">
<h4>{{ contents.title }}</h4> <h4>{{ contents.title }}</h4>
<p v-html="contents.content"></p> <p v-html="contents.content"></p>
......
/* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
import bp from '../../../breakpoints'; import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
...@@ -56,8 +54,11 @@ gl.issueBoards.ModalList = Vue.extend({ ...@@ -56,8 +54,11 @@ gl.issueBoards.ModalList = Vue.extend({
scrollHandler() { scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage); const currentPage = Math.floor(this.issues.length / this.perPage);
if ((this.scrollTop() > this.scrollHeight() - 100) && !this.loadingNewPage if (
&& currentPage === this.page) { this.scrollTop() > this.scrollHeight() - 100 &&
!this.loadingNewPage &&
currentPage === this.page
) {
this.loadingNewPage = true; this.loadingNewPage = true;
this.page += 1; this.page += 1;
} }
......
<script> <script>
/* global ListIssue */ import $ from 'jquery';
import _ from 'underscore';
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Api from '../../api';
import $ from 'jquery'; export default {
import _ from 'underscore'; name: 'BoardProjectSelect',
import eventHub from '../eventhub'; components: {
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; loadingIcon,
import Api from '../../api'; },
props: {
export default { groupId: {
name: 'BoardProjectSelect', type: Number,
components: { required: true,
loadingIcon, default: 0,
},
props: {
groupId: {
type: Number,
required: true,
default: 0,
},
}, },
data() { },
return { data() {
loading: true, return {
selectedProject: {}, loading: true,
}; selectedProject: {},
};
},
computed: {
selectedProjectName() {
return this.selectedProject.name || 'Select a project';
}, },
computed: { },
selectedProjectName() { mounted() {
return this.selectedProject.name || 'Select a project'; $(this.$refs.projectsDropdown).glDropdown({
filterable: true,
filterRemote: true,
search: {
fields: ['name_with_namespace'],
}, },
}, clicked: ({ $el, e }) => {
mounted() { e.preventDefault();
$(this.$refs.projectsDropdown).glDropdown({ this.selectedProject = {
filterable: true, id: $el.data('project-id'),
filterRemote: true, name: $el.data('project-name'),
search: { };
fields: ['name_with_namespace'], eventHub.$emit('setSelectedProject', this.selectedProject);
}, },
clicked: ({ $el, e }) => { selectable: true,
e.preventDefault(); data: (term, callback) => {
this.selectedProject = { this.loading = true;
id: $el.data('project-id'), return Api.groupProjects(this.groupId, term, projects => {
name: $el.data('project-name'), this.loading = false;
}; callback(projects);
eventHub.$emit('setSelectedProject', this.selectedProject); });
}, },
selectable: true, renderRow(project) {
data: (term, callback) => { return `
this.loading = true;
return Api.groupProjects(this.groupId, term, (projects) => {
this.loading = false;
callback(projects);
});
},
renderRow(project) {
return `
<li> <li>
<a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}"> <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
${_.escape(project.name)} ${_.escape(project.name)}
</a> </a>
</li> </li>
`; `;
}, },
text: project => project.name, text: project => project.name,
}); });
}, },
}; };
</script> </script>
<template> <template>
......
/* global monaco */
import Disposable from './disposable'; import Disposable from './disposable';
import eventHub from '../../eventhub'; import eventHub from '../../eventhub';
......
...@@ -84,11 +84,11 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive ...@@ -84,11 +84,11 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive
}); });
}; };
export const setFileMrChange = ({ state, commit }, { file, mrChange }) => { export const setFileMrChange = ({ commit }, { file, mrChange }) => {
commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange }); commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange });
}; };
export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) => { export const getRawFileData = ({ state, commit }, { path, baseSha }) => {
const file = state.entries[path]; const file = state.entries[path];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
service service
...@@ -156,7 +156,7 @@ export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn ...@@ -156,7 +156,7 @@ export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn
} }
}; };
export const setFileViewMode = ({ state, commit }, { file, viewMode }) => { export const setFileViewMode = ({ commit }, { file, viewMode }) => {
commit(types.SET_FILE_VIEWMODE, { file, viewMode }); commit(types.SET_FILE_VIEWMODE, { file, viewMode });
}; };
......
...@@ -3,7 +3,7 @@ import service from '../../services'; ...@@ -3,7 +3,7 @@ import service from '../../services';
import * as types from '../mutation_types'; import * as types from '../mutation_types';
export const getMergeRequestData = ( export const getMergeRequestData = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
...@@ -32,7 +32,7 @@ export const getMergeRequestData = ( ...@@ -32,7 +32,7 @@ export const getMergeRequestData = (
}); });
export const getMergeRequestChanges = ( export const getMergeRequestChanges = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
...@@ -58,7 +58,7 @@ export const getMergeRequestChanges = ( ...@@ -58,7 +58,7 @@ export const getMergeRequestChanges = (
}); });
export const getMergeRequestVersions = ( export const getMergeRequestVersions = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
......
...@@ -7,10 +7,7 @@ import Poll from '../../../lib/utils/poll'; ...@@ -7,10 +7,7 @@ import Poll from '../../../lib/utils/poll';
let eTagPoll; let eTagPoll;
export const getProjectData = ( export const getProjectData = ({ commit, state }, { namespace, projectId, force = false } = {}) =>
{ commit, state, dispatch },
{ namespace, projectId, force = false } = {},
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (!state.projects[`${namespace}/${projectId}`] || force) { if (!state.projects[`${namespace}/${projectId}`] || force) {
commit(types.TOGGLE_LOADING, { entry: state }); commit(types.TOGGLE_LOADING, { entry: state });
...@@ -40,10 +37,7 @@ export const getProjectData = ( ...@@ -40,10 +37,7 @@ export const getProjectData = (
} }
}); });
export const getBranchData = ( export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
{ commit, state, dispatch },
{ projectId, branchId, force = false } = {},
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if ( if (
typeof state.projects[`${projectId}`] === 'undefined' || typeof state.projects[`${projectId}`] === 'undefined' ||
...@@ -78,7 +72,7 @@ export const getBranchData = ( ...@@ -78,7 +72,7 @@ export const getBranchData = (
} }
}); });
export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId, branchId } = {}) => export const refreshLastCommitData = ({ commit }, { projectId, branchId } = {}) =>
service service
.getBranchData(projectId, branchId) .getBranchData(projectId, branchId)
.then(({ data }) => { .then(({ data }) => {
...@@ -92,7 +86,7 @@ export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId, ...@@ -92,7 +86,7 @@ export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId,
flash(__('Error loading last commit.'), 'alert', document, null, false, true); flash(__('Error loading last commit.'), 'alert', document, null, false, true);
}); });
export const pollSuccessCallBack = ({ commit, state, dispatch }, { data }) => { export const pollSuccessCallBack = ({ commit, state }, { data }) => {
if (data.pipelines && data.pipelines.length) { if (data.pipelines && data.pipelines.length) {
const lastCommitHash = const lastCommitHash =
state.projects[state.currentProjectId].branches[state.currentBranchId].commit.id; state.projects[state.currentProjectId].branches[state.currentBranchId].commit.id;
......
...@@ -5,7 +5,7 @@ import * as types from '../mutation_types'; ...@@ -5,7 +5,7 @@ import * as types from '../mutation_types';
import { findEntry } from '../utils'; import { findEntry } from '../utils';
import FilesDecoratorWorker from '../workers/files_decorator_worker'; import FilesDecoratorWorker from '../workers/files_decorator_worker';
export const toggleTreeOpen = ({ commit, dispatch }, path) => { export const toggleTreeOpen = ({ commit }, path) => {
commit(types.TOGGLE_TREE_OPEN, path); commit(types.TOGGLE_TREE_OPEN, path);
}; };
...@@ -23,7 +23,7 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => { ...@@ -23,7 +23,7 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
} }
}; };
export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { export const getLastCommitData = ({ state, commit, dispatch }, tree = state) => {
if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return; if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return;
service service
...@@ -49,7 +49,7 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s ...@@ -49,7 +49,7 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s
.catch(() => flash('Error fetching log data.', 'alert', document, null, false, true)); .catch(() => flash('Error fetching log data.', 'alert', document, null, false, true));
}; };
export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } = {}) => export const getFiles = ({ state, commit }, { projectId, branchId } = {}) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (!state.trees[`${projectId}/${branchId}`]) { if (!state.trees[`${projectId}/${branchId}`]) {
const selectedProject = state.projects[projectId]; const selectedProject = state.projects[projectId];
......
...@@ -31,9 +31,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => { ...@@ -31,9 +31,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
const currentProject = rootState.projects[rootState.currentProjectId]; const currentProject = rootState.projects[rootState.currentProjectId];
const commitStats = data.stats const commitStats = data.stats
? sprintf(__('with %{additions} additions, %{deletions} deletions.'), { ? sprintf(__('with %{additions} additions, %{deletions} deletions.'), {
additions: data.stats.additions, // eslint-disable-line indent additions: data.stats.additions, // eslint-disable-line indent-legacy
deletions: data.stats.deletions, // eslint-disable-line indent deletions: data.stats.deletions, // eslint-disable-line indent-legacy
}) // eslint-disable-line indent }) // eslint-disable-line indent-legacy
: ''; : '';
const commitMsg = sprintf( const commitMsg = sprintf(
__('Your changes have been committed. Commit %{commitId} %{commitStats}'), __('Your changes have been committed. Commit %{commitId} %{commitStats}'),
...@@ -74,10 +74,7 @@ export const checkCommitStatus = ({ rootState }) => ...@@ -74,10 +74,7 @@ export const checkCommitStatus = ({ rootState }) =>
), ),
); );
export const updateFilesAfterCommit = ( export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }) => {
{ commit, dispatch, state, rootState, rootGetters },
{ data },
) => {
const selectedProject = rootState.projects[rootState.currentProjectId]; const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = { const lastCommit = {
commit_path: `${selectedProject.web_url}/commit/${data.id}`, commit_path: `${selectedProject.web_url}/commit/${data.id}`,
......
...@@ -84,7 +84,7 @@ export default class Job { ...@@ -84,7 +84,7 @@ export default class Job {
If the browser does not support position sticky, it returns the position as static. If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not If the browser does support sticky, then we allow the browser to handle it, if not
then we use a polyfill then we use a polyfill
**/ */
if (this.$topBar.css('position') !== 'static') return; if (this.$topBar.css('position') !== 'static') return;
StickyFill.add(this.$topBar); StickyFill.add(this.$topBar);
......
/* global Build */
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Flash from '../flash'; import Flash from '../flash';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
...@@ -50,7 +48,8 @@ export default class JobMediator { ...@@ -50,7 +48,8 @@ export default class JobMediator {
} }
getJob() { getJob() {
return this.service.getJob() return this.service
.getJob()
.then(response => this.successCallback(response)) .then(response => this.successCallback(response))
.catch(() => this.errorCallback()); .catch(() => this.errorCallback());
} }
......
...@@ -9,7 +9,7 @@ delete window.translations; ...@@ -9,7 +9,7 @@ delete window.translations;
Translates `text` Translates `text`
@param text The text to be translated @param text The text to be translated
@returns {String} The translated text @returns {String} The translated text
**/ */
const gettext = locale.gettext.bind(locale); const gettext = locale.gettext.bind(locale);
/** /**
...@@ -21,7 +21,7 @@ const gettext = locale.gettext.bind(locale); ...@@ -21,7 +21,7 @@ const gettext = locale.gettext.bind(locale);
@param pluralText Plural text to translate (eg. '%d days') @param pluralText Plural text to translate (eg. '%d days')
@param count Number to decide which translation to use (eg. 2) @param count Number to decide which translation to use (eg. 2)
@returns {String} Translated text with the number replaced (eg. '2 days') @returns {String} Translated text with the number replaced (eg. '2 days')
**/ */
const ngettext = (text, pluralText, count) => { const ngettext = (text, pluralText, count) => {
const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|'); const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|');
...@@ -38,7 +38,7 @@ const ngettext = (text, pluralText, count) => { ...@@ -38,7 +38,7 @@ const ngettext = (text, pluralText, count) => {
(eg. 'Context') (eg. 'Context')
@param key Is the dynamic variable you want to be translated @param key Is the dynamic variable you want to be translated
@returns {String} Translated context based text @returns {String} Translated context based text
**/ */
const pgettext = (keyOrContext, key) => { const pgettext = (keyOrContext, key) => {
const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext; const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext;
const translated = gettext(normalizedKey).split('|'); const translated = gettext(normalizedKey).split('|');
......
...@@ -10,7 +10,7 @@ import _ from 'underscore'; ...@@ -10,7 +10,7 @@ import _ from 'underscore';
@see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf @see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
@see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992 @see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
**/ */
export default (input, parameters, escapeParameters = true) => { export default (input, parameters, escapeParameters = true) => {
let output = input; let output = input;
......
...@@ -427,7 +427,7 @@ export default class MergeRequestTabs { ...@@ -427,7 +427,7 @@ export default class MergeRequestTabs {
If the browser does not support position sticky, it returns the position as static. If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not If the browser does support sticky, then we allow the browser to handle it, if not
then we default back to Bootstraps affix then we default back to Bootstraps affix
**/ */
if ($tabs.css('position') !== 'static') return; if ($tabs.css('position') !== 'static') return;
const $diffTabs = $('#diff-notes-app'); const $diffTabs = $('#diff-notes-app');
......
...@@ -12,20 +12,13 @@ import { isInViewport, scrollToElement } from '../../lib/utils/common_utils'; ...@@ -12,20 +12,13 @@ import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
let eTagPoll; let eTagPoll;
export const setNotesData = ({ commit }, data) => export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
commit(types.SET_NOTES_DATA, data); export const setNoteableData = ({ commit }, data) => commit(types.SET_NOTEABLE_DATA, data);
export const setNoteableData = ({ commit }, data) => export const setUserData = ({ commit }, data) => commit(types.SET_USER_DATA, data);
commit(types.SET_NOTEABLE_DATA, data); export const setLastFetchedAt = ({ commit }, data) => commit(types.SET_LAST_FETCHED_AT, data);
export const setUserData = ({ commit }, data) => export const setInitialNotes = ({ commit }, data) => commit(types.SET_INITIAL_NOTES, data);
commit(types.SET_USER_DATA, data); export const setTargetNoteHash = ({ commit }, data) => commit(types.SET_TARGET_NOTE_HASH, data);
export const setLastFetchedAt = ({ commit }, data) => export const toggleDiscussion = ({ commit }, data) => commit(types.TOGGLE_DISCUSSION, data);
commit(types.SET_LAST_FETCHED_AT, data);
export const setInitialNotes = ({ commit }, data) =>
commit(types.SET_INITIAL_NOTES, data);
export const setTargetNoteHash = ({ commit }, data) =>
commit(types.SET_TARGET_NOTE_HASH, data);
export const toggleDiscussion = ({ commit }, data) =>
commit(types.TOGGLE_DISCUSSION, data);
export const fetchNotes = ({ commit }, path) => export const fetchNotes = ({ commit }, path) =>
service service
...@@ -69,20 +62,14 @@ export const createNewNote = ({ commit }, { endpoint, data }) => ...@@ -69,20 +62,14 @@ export const createNewNote = ({ commit }, { endpoint, data }) =>
return res; return res;
}); });
export const removePlaceholderNotes = ({ commit }) => export const removePlaceholderNotes = ({ commit }) => commit(types.REMOVE_PLACEHOLDER_NOTES);
commit(types.REMOVE_PLACEHOLDER_NOTES);
export const toggleResolveNote = ( export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion }) =>
{ commit },
{ endpoint, isResolved, discussion },
) =>
service service
.toggleResolveNote(endpoint, isResolved) .toggleResolveNote(endpoint, isResolved)
.then(res => res.json()) .then(res => res.json())
.then(res => { .then(res => {
const mutationType = discussion const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
? types.UPDATE_DISCUSSION
: types.UPDATE_NOTE;
commit(mutationType, res); commit(mutationType, res);
}); });
...@@ -114,7 +101,7 @@ export const reopenIssue = ({ commit, dispatch, state }) => { ...@@ -114,7 +101,7 @@ export const reopenIssue = ({ commit, dispatch, state }) => {
export const toggleStateButtonLoading = ({ commit }, value) => export const toggleStateButtonLoading = ({ commit }, value) =>
commit(types.TOGGLE_STATE_BUTTON_LOADING, value); commit(types.TOGGLE_STATE_BUTTON_LOADING, value);
export const emitStateChangedEvent = ({ commit, getters }, data) => { export const emitStateChangedEvent = ({ getters }, data) => {
const event = new CustomEvent('issuable_vue_app:change', { const event = new CustomEvent('issuable_vue_app:change', {
detail: { detail: {
data, data,
...@@ -179,10 +166,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -179,10 +166,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
loadAwardsHandler() loadAwardsHandler()
.then(awardsHandler => { .then(awardsHandler => {
awardsHandler.addAwardToEmojiBar( awardsHandler.addAwardToEmojiBar(votesBlock, commandsChanges.emoji_award);
votesBlock,
commandsChanges.emoji_award,
);
awardsHandler.scrollToAwards(); awardsHandler.scrollToAwards();
}) })
.catch(() => { .catch(() => {
...@@ -194,10 +178,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -194,10 +178,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
}); });
} }
if ( if (commandsChanges.spend_time != null || commandsChanges.time_estimate != null) {
commandsChanges.spend_time != null ||
commandsChanges.time_estimate != null
) {
sidebarTimeTrackingEventHub.$emit('timeTrackingUpdated', res); sidebarTimeTrackingEventHub.$emit('timeTrackingUpdated', res);
} }
} }
...@@ -218,14 +199,8 @@ const pollSuccessCallBack = (resp, commit, state, getters) => { ...@@ -218,14 +199,8 @@ const pollSuccessCallBack = (resp, commit, state, getters) => {
resp.notes.forEach(note => { resp.notes.forEach(note => {
if (notesById[note.id]) { if (notesById[note.id]) {
commit(types.UPDATE_NOTE, note); commit(types.UPDATE_NOTE, note);
} else if ( } else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
note.type === constants.DISCUSSION_NOTE || const discussion = utils.findNoteObjectById(state.notes, note.discussion_id);
note.type === constants.DIFF_NOTE
) {
const discussion = utils.findNoteObjectById(
state.notes,
note.discussion_id,
);
if (discussion) { if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note); commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
...@@ -249,11 +224,8 @@ export const poll = ({ commit, state, getters }) => { ...@@ -249,11 +224,8 @@ export const poll = ({ commit, state, getters }) => {
method: 'poll', method: 'poll',
data: state, data: state,
successCallback: resp => successCallback: resp =>
resp resp.json().then(data => pollSuccessCallBack(data, commit, state, getters)),
.json() errorCallback: () => Flash('Something went wrong while fetching latest comments.'),
.then(data => pollSuccessCallBack(data, commit, state, getters)),
errorCallback: () =>
Flash('Something went wrong while fetching latest comments.'),
}); });
if (!Visibility.hidden()) { if (!Visibility.hidden()) {
...@@ -292,14 +264,11 @@ export const fetchData = ({ commit, state, getters }) => { ...@@ -292,14 +264,11 @@ export const fetchData = ({ commit, state, getters }) => {
.catch(() => Flash('Something went wrong while fetching latest comments.')); .catch(() => Flash('Something went wrong while fetching latest comments.'));
}; };
export const toggleAward = ( export const toggleAward = ({ commit, getters }, { awardName, noteId }) => {
{ commit, state, getters, dispatch },
{ awardName, noteId },
) => {
commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] }); commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] });
}; };
export const toggleAwardRequest = ({ commit, getters, dispatch }, data) => { export const toggleAwardRequest = ({ dispatch }, data) => {
const { endpoint, awardName } = data; const { endpoint, awardName } = data;
return service return service
......
...@@ -5,7 +5,7 @@ import $ from 'jquery'; ...@@ -5,7 +5,7 @@ import $ from 'jquery';
* *
* Toggling this checkbox adds/removes a `remember_me` parameter to the * Toggling this checkbox adds/removes a `remember_me` parameter to the
* login buttons' href, which is passed on to the omniauth callback. * login buttons' href, which is passed on to the omniauth callback.
**/ */
export default class OAuthRememberMe { export default class OAuthRememberMe {
constructor(opts = {}) { constructor(opts = {}) {
......
...@@ -37,7 +37,7 @@ const IGNORE_URLS = [ ...@@ -37,7 +37,7 @@ const IGNORE_URLS = [
/extensions\//i, /extensions\//i,
/^chrome:\/\//i, /^chrome:\/\//i,
// Other plugins // Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i, /webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i, /metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
]; ];
......
...@@ -7,9 +7,10 @@ Vue.use(VueResource); ...@@ -7,9 +7,10 @@ Vue.use(VueResource);
export const fetchRepos = ({ commit, state }) => { export const fetchRepos = ({ commit, state }) => {
commit(types.TOGGLE_MAIN_LOADING); commit(types.TOGGLE_MAIN_LOADING);
return Vue.http.get(state.endpoint) return Vue.http
.get(state.endpoint)
.then(res => res.json()) .then(res => res.json())
.then((response) => { .then(response => {
commit(types.TOGGLE_MAIN_LOADING); commit(types.TOGGLE_MAIN_LOADING);
commit(types.SET_REPOS_LIST, response); commit(types.SET_REPOS_LIST, response);
}); });
...@@ -18,19 +19,20 @@ export const fetchRepos = ({ commit, state }) => { ...@@ -18,19 +19,20 @@ export const fetchRepos = ({ commit, state }) => {
export const fetchList = ({ commit }, { repo, page }) => { export const fetchList = ({ commit }, { repo, page }) => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
return Vue.http.get(repo.tagsPath, { params: { page } }) return Vue.http.get(repo.tagsPath, { params: { page } }).then(response => {
.then((response) => { const headers = response.headers;
const headers = response.headers;
return response.json().then((resp) => { return response.json().then(resp => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
commit(types.SET_REGISTRY_LIST, { repo, resp, headers }); commit(types.SET_REGISTRY_LIST, { repo, resp, headers });
});
}); });
});
}; };
// eslint-disable-next-line no-unused-vars
export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath); export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath);
// eslint-disable-next-line no-unused-vars
export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath); export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath);
export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data);
......
...@@ -13,7 +13,7 @@ export default (Vue) => { ...@@ -13,7 +13,7 @@ export default (Vue) => {
@param text The text to be translated @param text The text to be translated
@returns {String} The translated text @returns {String} The translated text
**/ */
__, __,
/** /**
Translate the text with a number Translate the text with a number
...@@ -24,7 +24,7 @@ export default (Vue) => { ...@@ -24,7 +24,7 @@ export default (Vue) => {
@param pluralText Plural text to translate (eg. '%d days') @param pluralText Plural text to translate (eg. '%d days')
@param count Number to decide which translation to use (eg. 2) @param count Number to decide which translation to use (eg. 2)
@returns {String} Translated text with the number replaced (eg. '2 days') @returns {String} Translated text with the number replaced (eg. '2 days')
**/ */
n__, n__,
/** /**
Translate context based text Translate context based text
...@@ -36,7 +36,7 @@ export default (Vue) => { ...@@ -36,7 +36,7 @@ export default (Vue) => {
(eg. 'Context') (eg. 'Context')
@param key Is the dynamic variable you want to be translated @param key Is the dynamic variable you want to be translated
@returns {String} Translated context based text @returns {String} Translated context based text
**/ */
s__, s__,
sprintf, sprintf,
}, },
......
...@@ -74,12 +74,6 @@ body.modal-open { ...@@ -74,12 +74,6 @@ body.modal-open {
} }
} }
@include media-breakpoint-up(lg) {
.modal-full {
width: 98%;
}
}
.modal { .modal {
background-color: $black-transparent; background-color: $black-transparent;
z-index: 2100; z-index: 2100;
......
module Groups
class SharedProjectsController < Groups::ApplicationController
respond_to :json
before_action :group
skip_cross_project_access_check :index
def index
shared_projects = GroupProjectsFinder.new(
group: group,
current_user: current_user,
params: finder_params,
options: { only_shared: true }
).execute
serializer = GroupChildSerializer.new(current_user: current_user)
.with_pagination(request, response)
render json: serializer.represent(shared_projects)
end
private
def finder_params
@finder_params ||= begin
# Make the `search` param consistent for the frontend,
# which will be using `filter`.
params[:search] ||= params[:filter] if params[:filter]
params.permit(:sort, :search)
end
end
end
end
...@@ -957,6 +957,14 @@ class Repository ...@@ -957,6 +957,14 @@ class Repository
remote_branch: merge_request.target_branch) remote_branch: merge_request.target_branch)
end end
def blob_data_at(sha, path)
blob = blob_at(sha, path)
return unless blob
blob.load_all_data!
blob.data
end
def squash(user, merge_request) def squash(user, merge_request)
raw.squash(user, merge_request.id, branch: merge_request.target_branch, raw.squash(user, merge_request.id, branch: merge_request.target_branch,
start_sha: merge_request.diff_start_sha, start_sha: merge_request.diff_start_sha,
...@@ -979,14 +987,6 @@ class Repository ...@@ -979,14 +987,6 @@ class Repository
::Commit.new(commit, @project) if commit ::Commit.new(commit, @project) if commit
end end
def blob_data_at(sha, path)
blob = blob_at(sha, path)
return unless blob
blob.load_all_data!
blob.data
end
def cache def cache
@cache ||= Gitlab::RepositoryCache.new(self) @cache ||= Gitlab::RepositoryCache.new(self)
end end
......
...@@ -206,10 +206,11 @@ class Service < ActiveRecord::Base ...@@ -206,10 +206,11 @@ class Service < ActiveRecord::Base
args.each do |arg| args.each do |arg|
class_eval %{ class_eval %{
def #{arg}? def #{arg}?
# '!!' is used because nil or empty string is converted to nil
if Gitlab.rails5? if Gitlab.rails5?
!ActiveModel::Type::Boolean::FALSE_VALUES.include?(#{arg}) !!ActiveRecord::Type::Boolean.new.cast(#{arg})
else else
ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) !!ActiveRecord::Type::Boolean.new.type_cast_from_database(#{arg})
end end
end end
} }
......
...@@ -31,7 +31,7 @@ class GroupChildEntity < Grape::Entity ...@@ -31,7 +31,7 @@ class GroupChildEntity < Grape::Entity
end end
# Project only attributes # Project only attributes
expose :star_count, expose :star_count, :archived,
if: lambda { |_instance, _options| project? } if: lambda { |_instance, _options| project? }
# Group only attributes # Group only attributes
......
...@@ -11,6 +11,7 @@ module ObjectStorage ...@@ -11,6 +11,7 @@ module ObjectStorage
ObjectStorageUnavailable = Class.new(StandardError) ObjectStorageUnavailable = Class.new(StandardError)
DIRECT_UPLOAD_TIMEOUT = 4.hours DIRECT_UPLOAD_TIMEOUT = 4.hours
DIRECT_UPLOAD_EXPIRE_OFFSET = 15.minutes
TMP_UPLOAD_PATH = 'tmp/uploads'.freeze TMP_UPLOAD_PATH = 'tmp/uploads'.freeze
module Store module Store
...@@ -174,11 +175,12 @@ module ObjectStorage ...@@ -174,11 +175,12 @@ module ObjectStorage
id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-') id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-')
upload_path = File.join(TMP_UPLOAD_PATH, id) upload_path = File.join(TMP_UPLOAD_PATH, id)
connection = ::Fog::Storage.new(self.object_store_credentials) connection = ::Fog::Storage.new(self.object_store_credentials)
expire_at = Time.now + DIRECT_UPLOAD_TIMEOUT expire_at = Time.now + DIRECT_UPLOAD_TIMEOUT + DIRECT_UPLOAD_EXPIRE_OFFSET
options = { 'Content-Type' => 'application/octet-stream' } options = { 'Content-Type' => 'application/octet-stream' }
{ {
ID: id, ID: id,
Timeout: DIRECT_UPLOAD_TIMEOUT,
GetURL: connection.get_object_url(remote_store_path, upload_path, expire_at), GetURL: connection.get_object_url(remote_store_path, upload_path, expire_at),
DeleteURL: connection.delete_object_url(remote_store_path, upload_path, expire_at), DeleteURL: connection.delete_object_url(remote_store_path, upload_path, expire_at),
StoreURL: connection.put_object_url(remote_store_path, upload_path, expire_at, options) StoreURL: connection.put_object_url(remote_store_path, upload_path, expire_at, options)
......
- active_tab = local_assigns.fetch(:active_tab, 'blank') - active_tab = local_assigns.fetch(:active_tab, 'blank')
- f = local_assigns.fetch(:f) - f = local_assigns.fetch(:f)
.project-import.row .project-import
.col-lg-12 .form-group.import-btn-container.clearfix
.form-group.import-btn-container.clearfix = f.label :visibility_level, class: 'label-light' do #the label here seems wrong
= f.label :visibility_level, class: 'label-light' do #the label here seems wrong Import project from
Import project from .import-buttons
.import-buttons - if gitlab_project_import_enabled?
- if gitlab_project_import_enabled? .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
.import_gitlab_project.has-tooltip{ data: { container: 'body' } } = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = icon('gitlab', text: 'GitLab export')
= icon('gitlab', text: 'GitLab export') %div
%div - if github_import_enabled?
- if github_import_enabled? = link_to new_import_github_path, class: 'btn js-import-github' do
= link_to new_import_github_path, class: 'btn js-import-github' do = icon('github', text: 'GitHub')
= icon('github', text: 'GitHub') %div
%div - if bitbucket_import_enabled?
- if bitbucket_import_enabled? = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do
= link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do = icon('bitbucket', text: 'Bitbucket')
= icon('bitbucket', text: 'Bitbucket') - unless bitbucket_import_configured?
- unless bitbucket_import_configured? = render 'bitbucket_import_modal'
= render 'bitbucket_import_modal' %div
%div - if gitlab_import_enabled?
- if gitlab_import_enabled? = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do
= link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do = icon('gitlab', text: 'GitLab.com')
= icon('gitlab', text: 'GitLab.com') - unless gitlab_import_configured?
- unless gitlab_import_configured? = render 'gitlab_import_modal'
= render 'gitlab_import_modal' %div
%div - if google_code_import_enabled?
- if google_code_import_enabled? = link_to new_import_google_code_path, class: 'btn import_google_code' do
= link_to new_import_google_code_path, class: 'btn import_google_code' do = icon('google', text: 'Google Code')
= icon('google', text: 'Google Code') %div
%div - if fogbugz_import_enabled?
- if fogbugz_import_enabled? = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do = icon('bug', text: 'Fogbugz')
= icon('bug', text: 'Fogbugz') %div
%div - if gitea_import_enabled?
- if gitea_import_enabled? = link_to new_import_gitea_path, class: 'btn import_gitea' do
= link_to new_import_gitea_path, class: 'btn import_gitea' do = custom_icon('go_logo')
= custom_icon('go_logo') Gitea
Gitea %div
%div - if git_import_enabled?
- if git_import_enabled? %button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } }
%button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } } = icon('git', text: 'Repo by URL')
= icon('git', text: 'Repo by URL') .js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
.col-lg-12 %hr
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') } = render "shared/import_form", f: f
%hr = render 'new_project_fields', f: f, project_name_id: "import-url-name"
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
...@@ -35,11 +35,10 @@ ...@@ -35,11 +35,10 @@
%span (optional) %span (optional)
= f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250 = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250
.form-group.visibility-level-setting = f.label :visibility_level, class: 'label-light' do
= f.label :visibility_level, class: 'label-light' do Visibility Level
Visibility Level = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer' = render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
.row .row
.col-sm-8.offset-sm-4.signin-with-google .col-sm-8.offset-sm-4.signin-with-google
- if @authorize_url - if @authorize_url
= link_to @authorize_url do = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
= image_tag('auth_buttons/signin_with_google.png', width: '191px')
= _('or') = _('or')
= link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer') = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
- else - else
......
- ci_cd_only = local_assigns.fetch(:ci_cd_only, false) - ci_cd_only = local_assigns.fetch(:ci_cd_only, false)
.form-group.row.import-url-data .form-group.import-url-data
= f.label :import_url, class: 'label-light' do = f.label :import_url, class: 'label-light' do
%span %span
= _('Git repository URL') = _('Git repository URL')
= f.text_field :import_url, autocomplete: 'off', class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', required: true = f.text_field :import_url, autocomplete: 'off', class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', required: true
.card.prepend-top-20 .info-well.prepend-top-20
%ul .well-segment
%li %ul
= _('The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>.').html_safe %li
%li = _('The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>.').html_safe
= _('If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.').html_safe %li
%li = _('If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.').html_safe
= import_will_timeout_message(ci_cd_only) %li
%li = import_will_timeout_message(ci_cd_only)
= import_svn_message(ci_cd_only) %li
= import_svn_message(ci_cd_only)
- with_label = local_assigns.fetch(:with_label, true) - with_label = local_assigns.fetch(:with_label, true)
.form-group.row.visibility-level-setting .form-group.visibility-level-setting
- if with_label - if with_label
= f.label :visibility_level, class: 'col-form-label col-sm-2' do = f.label :visibility_level, class: 'col-form-label col-sm-2' do
Visibility Level Visibility Level
......
.row.empty-state.text-center .row.empty-state.text-center
.col-xs-12 .col-12
.svg-130.prepend-top-default .svg-130.prepend-top-default
= image_tag 'illustrations/issue-dashboard_results-without-filter.svg' = image_tag 'illustrations/issue-dashboard_results-without-filter.svg'
.col-xs-12 .col-12
.text-content .text-content
%h4 %h4
= _("Please select at least one filter to see results") = _("Please select at least one filter to see results")
.row.empty-state .row.empty-state
.col-xs-12 .col-12
.svg-content .svg-content
= image_tag image_path = image_tag image_path
.col-xs-12 .col-12
.text-content.text-center .text-content.text-center
= yield = yield
---
title: Replace Gitlab::REVISION with Gitlab.revision and handle installations without
a .git directory
merge_request: 19125
author:
type: fixed
---
title: Fix UI broken in line profiling modal due to Bootstrap 4
merge_request: 19253
author: Takuya Noguchi
type: other
---
title: Updates the version of kubeclient from 3.0 to 3.1.0
merge_request: 19199
author:
type: other
---
title: Only preload member records for the relevant projects/groups/user in projects
API
merge_request:
author:
type: performance
---
title: Fix remote mirror database inconsistencies when upgrading from EE to CE
merge_request: 19196
author:
type: fixed
---
title: Missing timeout value in object storage pre-authorization
merge_request: 19201
author:
type: fixed
--- ---
title: Fix local storage not being cleared after creating a new issue title: Fix &nbsp; after sign-in with Google button
merge_request: merge_request:
author: author:
type: fixed type: fixed
--- ---
title: Memoize Gitlab::Database.version title: Replace grape-route-helpers with our own grape-path-helpers
merge_request: merge_request:
author: author:
type: performance type: performance
---
title: Fix encoding of branch names on compare and new merge request page
merge_request: 19143
author:
type: fixed
if defined?(GrapeRouteHelpers)
module GrapeRouteHelpers
module AllRoutes
# Bringing in PR https://github.com/reprah/grape-route-helpers/pull/21 due to abandonment.
#
# Without the following fix, when two helper methods are the same, but have different arguments
# (for example: api_v1_cats_owners_path(id: 1) vs api_v1_cats_owners_path(id: 1, owner_id: 2))
# if the helper method with the least number of arguments is defined first (because the route was defined first)
# then it will shadow the longer route.
#
# The fix is to sort descending by amount of arguments
def decorated_routes
@decorated_routes ||= all_routes
.map { |r| DecoratedRoute.new(r) }
.sort_by { |r| -r.dynamic_path_segments.count }
end
end
class DecoratedRoute
# GrapeRouteHelpers gem tries to parse the versions
# from a string, not supporting Grape `version` array definition.
#
# Without the following fix, we get this on route helpers generation:
#
# => undefined method `scan' for ["v3", "v4"]
#
# 2.0.0 implementation of this method:
#
# ```
# def route_versions
# version_pattern = /[^\[",\]\s]+/
# if route_version
# route_version.scan(version_pattern)
# else
# [nil]
# end
# end
# ```
def route_versions
return [nil] if route_version.nil? || route_version.empty?
if route_version.is_a?(String)
version_pattern = /[^\[",\]\s]+/
route_version.scan(version_pattern)
else
route_version
end
end
end
end
end
...@@ -30,6 +30,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -30,6 +30,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :variables, only: [:show, :update] resource :variables, only: [:show, :update]
resources :children, only: [:index] resources :children, only: [:index]
resources :shared_projects, only: [:index]
resources :labels, except: [:show] do resources :labels, except: [:show] do
post :toggle_subscription, on: :member post :toggle_subscription, on: :member
......
...@@ -80,7 +80,7 @@ STDERR takes precedence over STDOUT. ...@@ -80,7 +80,7 @@ STDERR takes precedence over STDOUT.
![Custom message from custom Git hook](img/custom_hooks_error_msg.png) ![Custom message from custom Git hook](img/custom_hooks_error_msg.png)
[CI]: ../ci/readme.md [CI]: ../ci/README.md
[hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks [hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks
[webhooks]: ../user/project/integrations/webhooks.md [webhooks]: ../user/project/integrations/webhooks.md
[5073]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5073 [5073]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5073
......
...@@ -832,8 +832,8 @@ module API ...@@ -832,8 +832,8 @@ module API
class ProjectWithAccess < Project class ProjectWithAccess < Project
expose :permissions do expose :permissions do
expose :project_access, using: Entities::ProjectAccess do |project, options| expose :project_access, using: Entities::ProjectAccess do |project, options|
if options.key?(:project_members) if options[:project_members]
(options[:project_members] || []).find { |member| member.source_id == project.id } options[:project_members].find { |member| member.source_id == project.id }
else else
project.project_member(options[:current_user]) project.project_member(options[:current_user])
end end
...@@ -841,8 +841,8 @@ module API ...@@ -841,8 +841,8 @@ module API
expose :group_access, using: Entities::GroupAccess do |project, options| expose :group_access, using: Entities::GroupAccess do |project, options|
if project.group if project.group
if options.key?(:group_members) if options[:group_members]
(options[:group_members] || []).find { |member| member.source_id == project.namespace_id } options[:group_members].find { |member| member.source_id == project.namespace_id }
else else
project.group.group_member(options[:current_user]) project.group.group_member(options[:current_user])
end end
...@@ -853,13 +853,24 @@ module API ...@@ -853,13 +853,24 @@ module API
def self.preload_relation(projects_relation, options = {}) def self.preload_relation(projects_relation, options = {})
relation = super(projects_relation, options) relation = super(projects_relation, options)
unless options.key?(:group_members) # MySQL doesn't support LIMIT inside an IN subquery
relation = relation.preload(group: [group_members: [:source, user: [notification_settings: :source]]]) if Gitlab::Database.mysql?
project_ids = relation.pluck('projects.id')
namespace_ids = relation.pluck(:namespace_id)
else
project_ids = relation.select('projects.id')
namespace_ids = relation.select(:namespace_id)
end end
unless options.key?(:project_members) options[:project_members] = options[:current_user]
relation = relation.preload(project_members: [:source, user: [notification_settings: :source]]) .project_members
end .where(source_id: project_ids)
.preload(:source, user: [notification_settings: :source])
options[:group_members] = options[:current_user]
.group_members
.where(source_id: namespace_ids)
.preload(:source, user: [notification_settings: :source])
relation relation
end end
......
module API module API
module Helpers module Helpers
module RelatedResourcesHelpers module RelatedResourcesHelpers
include GrapeRouteHelpers::NamedRouteMatcher include GrapePathHelpers::NamedRouteMatcher
def issues_available?(project, options) def issues_available?(project, options)
available?(:issues, project, options[:current_user]) available?(:issues, project, options[:current_user])
......
...@@ -58,16 +58,9 @@ module API ...@@ -58,16 +58,9 @@ module API
projects = paginate(projects) projects = paginate(projects)
projects, options = with_custom_attributes(projects, options) projects, options = with_custom_attributes(projects, options)
if current_user
project_members = current_user.project_members.preload(:source, user: [notification_settings: :source])
group_members = current_user.group_members.preload(:source, user: [notification_settings: :source])
end
options = options.reverse_merge( options = options.reverse_merge(
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails, with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
statistics: params[:statistics], statistics: params[:statistics],
project_members: project_members,
group_members: group_members,
current_user: current_user current_user: current_user
) )
options[:with] = Entities::BasicProjectDetails if params[:simple] options[:with] = Entities::BasicProjectDetails if params[:simple]
......
...@@ -129,6 +129,7 @@ module API ...@@ -129,6 +129,7 @@ module API
optional :gitaly_timeout_default, type: Integer, desc: 'Default Gitaly timeout, in seconds. Set to 0 to disable timeouts.' optional :gitaly_timeout_default, type: Integer, desc: 'Default Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
optional :gitaly_timeout_medium, type: Integer, desc: 'Medium Gitaly timeout, in seconds. Set to 0 to disable timeouts.' optional :gitaly_timeout_medium, type: Integer, desc: 'Medium Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
optional :gitaly_timeout_fast, type: Integer, desc: 'Gitaly fast operation timeout, in seconds. Set to 0 to disable timeouts.' optional :gitaly_timeout_fast, type: Integer, desc: 'Gitaly fast operation timeout, in seconds. Set to 0 to disable timeouts.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction", optional :"#{type}_key_restriction",
......
...@@ -41,10 +41,10 @@ module Peek ...@@ -41,10 +41,10 @@ module Peek
] ]
end.sort_by{ |a,b,c,d,e,f| -f } end.sort_by{ |a,b,c,d,e,f| -f }
output = "<div class='modal-dialog modal-full'><div class='modal-content'>" output = "<div class='modal-dialog modal-lg'><div class='modal-content'>"
output << "<div class='modal-header'>" output << "<div class='modal-header'>"
output << "<button class='close btn btn-link btn-sm' type='button' data-dismiss='modal'>X</button>"
output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>" output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>"
output << "<button class='close' type='button' data-dismiss='modal' aria-label='close'><span aria-hidden='true'>&times;</span></button>"
output << "</div>" output << "</div>"
output << "<div class='modal-body'>" output << "<div class='modal-body'>"
......
require 'spec_helper'
describe Groups::SharedProjectsController do
def get_shared_projects(params = {})
get :index, params.reverse_merge(format: :json, group_id: group.full_path)
end
def share_project(project)
Projects::GroupLinks::CreateService.new(
project,
user,
link_group_access: ProjectGroupLink::DEVELOPER
).execute(group)
end
set(:group) { create(:group) }
set(:user) { create(:user) }
set(:shared_project) do
shared_project = create(:project, namespace: user.namespace)
share_project(shared_project)
shared_project
end
let(:json_project_ids) { json_response.map { |project_info| project_info['id'] } }
before do
sign_in(user)
end
describe 'GET #index' do
it 'returns only projects shared with the group' do
create(:project, namespace: group)
get_shared_projects
expect(json_project_ids).to contain_exactly(shared_project.id)
end
it 'allows filtering shared projects' do
project = create(:project, :archived, namespace: user.namespace, name: "Searching for")
share_project(project)
get_shared_projects(filter: 'search')
expect(json_project_ids).to contain_exactly(project.id)
end
it 'allows sorting projects' do
shared_project.update!(name: 'bbb')
second_project = create(:project, namespace: user.namespace, name: 'aaaa')
share_project(second_project)
get_shared_projects(sort: 'name_asc')
expect(json_project_ids).to eq([second_project.id, shared_project.id])
end
end
end
...@@ -592,7 +592,7 @@ describe 'Issues' do ...@@ -592,7 +592,7 @@ describe 'Issues' do
end end
it 'clears local storage after creating a new issue', :js do it 'clears local storage after creating a new issue', :js do
2.times.each do 2.times do
visit new_project_issue_path(project) visit new_project_issue_path(project)
wait_for_requests wait_for_requests
......
require 'spec_helper'
require_relative '../../config/initializers/grape_route_helpers_fix'
describe 'route shadowing' do
include GrapeRouteHelpers::NamedRouteMatcher
it 'does not occur' do
path = api_v4_projects_merge_requests_path(id: 1)
expect(path).to eq('/api/v4/projects/1/merge_requests')
path = api_v4_projects_merge_requests_path(id: 1, merge_request_iid: 3)
expect(path).to eq('/api/v4/projects/1/merge_requests/3')
end
end
{
"env": {
"jasmine": true
},
"extends": "plugin:jasmine/recommended",
"globals": {
"appendLoadFixtures": false,
"appendLoadStyleFixtures": false,
"appendSetFixtures": false,
"appendSetStyleFixtures": false,
"getJSONFixture": false,
"loadFixtures": false,
"loadJSONFixtures": false,
"loadStyleFixtures": false,
"preloadFixtures": false,
"preloadStyleFixtures": false,
"readFixtures": false,
"sandbox": false,
"setFixtures": false,
"setStyleFixtures": false,
"spyOnDependency": false,
"spyOnEvent": false,
"ClassSpecHelper": false
},
"plugins": ["jasmine"],
"rules": {
"func-names": 0,
"jasmine/no-suite-dupes": [1, "branch"],
"jasmine/no-spec-dupes": [1, "branch"],
"no-console": 0,
"prefer-arrow-callback": 0
}
}
---
env:
jasmine: true
extends: plugin:jasmine/recommended
globals:
appendLoadFixtures: false
appendLoadStyleFixtures: false
appendSetFixtures: false
appendSetStyleFixtures: false
getJSONFixture: false
loadFixtures: false
loadJSONFixtures: false
loadStyleFixtures: false
preloadFixtures: false
preloadStyleFixtures: false
readFixtures: false
sandbox: false
setFixtures: false
setStyleFixtures: false
spyOnDependency: false
spyOnEvent: false
ClassSpecHelper: false
plugins:
- jasmine
rules:
func-names: off
jasmine/no-suite-dupes:
- warn
- branch
jasmine/no-spec-dupes:
- warn
- branch
no-console: off
prefer-arrow-callback: off
...@@ -84,9 +84,14 @@ describe('iPython notebook renderer', () => { ...@@ -84,9 +84,14 @@ describe('iPython notebook renderer', () => {
describe('error in JSON response', () => { describe('error in JSON response', () => {
let mock; let mock;
beforeEach((done) => { beforeEach(done => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet('/test').reply(() => Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' })); mock
.onGet('/test')
.reply(() =>
// eslint-disable-next-line prefer-promise-reject-errors
Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }),
);
renderNotebook(); renderNotebook();
......
/* global BoardService */
import Vue from 'vue'; import Vue from 'vue';
import '~/boards/stores/boards_store'; import '~/boards/stores/boards_store';
import BoardBlankState from '~/boards/components/board_blank_state.vue'; import BoardBlankState from '~/boards/components/board_blank_state.vue';
......
/* global List */ /* global List */
/* global ListAssignee */ /* global ListAssignee */
/* global ListLabel */ /* global ListLabel */
/* global BoardService */
import Vue from 'vue'; import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
......
/* global BoardService */
/* global List */ /* global List */
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
......
/* global BoardService */
/* global List */ /* global List */
import Vue from 'vue'; import Vue from 'vue';
......
/* eslint-disable comma-dangle, one-var, no-unused-vars */ /* eslint-disable comma-dangle, one-var, no-unused-vars */
/* global BoardService */
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
......
/* eslint-disable comma-dangle */ /* eslint-disable comma-dangle */
/* global BoardService */
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
......
/* eslint-disable comma-dangle */ /* eslint-disable comma-dangle */
/* global BoardService */
/* global List */ /* global List */
/* global ListIssue */ /* global ListIssue */
......
...@@ -75,10 +75,7 @@ describe('Commit pipeline status component', () => { ...@@ -75,10 +75,7 @@ describe('Commit pipeline status component', () => {
describe('When polling data was not succesful', () => { describe('When polling data was not succesful', () => {
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(() => { mock.onGet('/dummy/endpoint').reply(502, {});
const res = Promise.reject([502, { }]);
return res;
});
vm = new Component({ vm = new Component({
props: { props: {
endpoint: '/dummy/endpoint', endpoint: '/dummy/endpoint',
......
import Vue from 'vue'; const mountComponent = (Component, props = {}, el = null) =>
new Component({
const mountComponent = (Component, props = {}, el = null) => new Component({ propsData: props,
propsData: props, }).$mount(el);
}).$mount(el);
export const createComponentWithStore = (Component, store, propsData = {}) => new Component({
store,
propsData,
});
export const createComponentWithMixin = (mixins = [], state = {}, props = {}, template = '<div></div>') => { export const createComponentWithStore = (Component, store, propsData = {}) =>
const Component = Vue.extend({ new Component({
template, store,
mixins, propsData,
data() {
return props;
},
}); });
return mountComponent(Component, props);
};
export const mountComponentWithStore = (Component, { el, props, store }) => export const mountComponentWithStore = (Component, { el, props, store }) =>
new Component({ new Component({
store, store,
propsData: props || { }, propsData: props || {},
}).$mount(el); }).$mount(el);
export default mountComponent; export default mountComponent;
...@@ -8,10 +8,7 @@ describe('Confidential Issue Sidebar Block', () => { ...@@ -8,10 +8,7 @@ describe('Confidential Issue Sidebar Block', () => {
beforeEach(() => { beforeEach(() => {
const Component = Vue.extend(confidentialIssueSidebar); const Component = Vue.extend(confidentialIssueSidebar);
const service = { const service = {
update: () => new Promise((resolve, reject) => { update: () => Promise.resolve(true),
resolve(true);
reject('failed!');
}),
}; };
vm1 = new Component({ vm1 = new Component({
......
/* eslint-disable prefer-rest-params, wrap-iife, /* eslint-disable prefer-rest-params, wrap-iife,
no-unused-expressions, no-return-assign, no-param-reassign*/ no-unused-expressions, no-return-assign, no-param-reassign */
export default class MockU2FDevice { export default class MockU2FDevice {
constructor() { constructor() {
......
...@@ -29,6 +29,18 @@ describe API::MergeRequests do ...@@ -29,6 +29,18 @@ describe API::MergeRequests do
project.add_reporter(user) project.add_reporter(user)
end end
describe 'route shadowing' do
include GrapePathHelpers::NamedRouteMatcher
it 'does not occur' do
path = api_v4_projects_merge_requests_path(id: 1)
expect(path).to eq('/api/v4/projects/1/merge_requests')
path = api_v4_projects_merge_requests_path(id: 1, merge_request_iid: 3)
expect(path).to eq('/api/v4/projects/1/merge_requests/3')
end
end
describe 'GET /merge_requests' do describe 'GET /merge_requests' do
context 'when unauthenticated' do context 'when unauthenticated' do
it 'returns an array of all merge requests' do it 'returns an array of all merge requests' do
......
...@@ -382,6 +382,8 @@ describe ObjectStorage do ...@@ -382,6 +382,8 @@ describe ObjectStorage do
is_expected.to have_key(:RemoteObject) is_expected.to have_key(:RemoteObject)
expect(subject[:RemoteObject]).to have_key(:ID) expect(subject[:RemoteObject]).to have_key(:ID)
expect(subject[:RemoteObject]).to include(Timeout: a_kind_of(Integer))
expect(subject[:RemoteObject][:Timeout]).to be(ObjectStorage::DIRECT_UPLOAD_TIMEOUT)
expect(subject[:RemoteObject]).to have_key(:GetURL) expect(subject[:RemoteObject]).to have_key(:GetURL)
expect(subject[:RemoteObject]).to have_key(:DeleteURL) expect(subject[:RemoteObject]).to have_key(:DeleteURL)
expect(subject[:RemoteObject]).to have_key(:StoreURL) expect(subject[:RemoteObject]).to have_key(:StoreURL)
......
This diff is collapsed.
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