Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
008921cd
Commit
008921cd
authored
Jun 30, 2021
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
f474a0e3
086e2e06
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
273 additions
and
80 deletions
+273
-80
app/assets/javascripts/boards/components/board_sidebar.js
app/assets/javascripts/boards/components/board_sidebar.js
+1
-1
app/assets/javascripts/boards/stores/actions.js
app/assets/javascripts/boards/stores/actions.js
+3
-3
app/assets/javascripts/diffs/components/diff_file.vue
app/assets/javascripts/diffs/components/diff_file.vue
+36
-25
app/assets/javascripts/diffs/components/diff_file_header.vue
app/assets/javascripts/diffs/components/diff_file_header.vue
+5
-4
app/assets/javascripts/diffs/components/diff_view.vue
app/assets/javascripts/diffs/components/diff_view.vue
+16
-11
app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
...sidebar/components/assignees/sidebar_assignees_widget.vue
+20
-2
app/workers/gitlab/import/stuck_import_job.rb
app/workers/gitlab/import/stuck_import_job.rb
+1
-1
spec/fast_spec_helper.rb
spec/fast_spec_helper.rb
+2
-0
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+2
-5
spec/frontend/diffs/components/diff_view_spec.js
spec/frontend/diffs/components/diff_view_spec.js
+4
-4
spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
...bar/components/assignees/sidebar_assignees_widget_spec.js
+31
-13
spec/spec_helper.rb
spec/spec_helper.rb
+3
-0
workhorse/internal/api/api.go
workhorse/internal/api/api.go
+46
-0
workhorse/internal/api/api_test.go
workhorse/internal/api/api_test.go
+74
-0
workhorse/internal/upstream/routes.go
workhorse/internal/upstream/routes.go
+1
-6
workhorse/internal/upstream/upstream.go
workhorse/internal/upstream/upstream.go
+28
-5
No files found.
app/assets/javascripts/boards/components/board_sidebar.js
View file @
008921cd
...
...
@@ -105,7 +105,7 @@ export default Vue.extend({
closeSidebar
()
{
this
.
detail
.
issue
=
{};
},
setAssignees
(
assignees
)
{
setAssignees
(
{
assignees
}
)
{
boardsStore
.
detail
.
issue
.
setAssignees
(
assignees
);
},
showScopedLabels
(
label
)
{
...
...
app/assets/javascripts/boards/stores/actions.js
View file @
008921cd
...
...
@@ -472,11 +472,11 @@ export default {
}
},
setAssignees
:
({
commit
,
getters
},
assigneeUsernames
)
=>
{
setAssignees
:
({
commit
},
{
id
,
assignees
}
)
=>
{
commit
(
'
UPDATE_BOARD_ITEM_BY_ID
'
,
{
itemId
:
getters
.
activeBoardItem
.
id
,
itemId
:
id
,
prop
:
'
assignees
'
,
value
:
assignee
Username
s
,
value
:
assignees
,
});
},
...
...
app/assets/javascripts/diffs/components/diff_file.vue
View file @
008921cd
...
...
@@ -2,6 +2,7 @@
import
{
GlButton
,
GlLoadingIcon
,
GlSafeHtmlDirective
as
SafeHtml
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
escape
}
from
'
lodash
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
IdState
}
from
'
vendor/vue-virtual-scroller
'
;
import
createFlash
from
'
~/flash
'
;
import
{
hasDiff
}
from
'
~/helpers/diffs_helper
'
;
import
{
diffViewerErrors
}
from
'
~/ide/constants
'
;
...
...
@@ -34,7 +35,7 @@ export default {
directives
:
{
SafeHtml
,
},
mixins
:
[
glFeatureFlagsMixin
()],
mixins
:
[
glFeatureFlagsMixin
()
,
IdState
({
idProp
:
(
vm
)
=>
vm
.
file
.
file_hash
})
],
props
:
{
file
:
{
type
:
Object
,
...
...
@@ -79,7 +80,7 @@ export default {
default
:
false
,
},
},
data
()
{
idState
()
{
return
{
isLoadingCollapsedDiff
:
false
,
forkMessageVisible
:
false
,
...
...
@@ -101,7 +102,9 @@ export default {
return
getShortShaFromFile
(
this
.
file
);
},
showLoadingIcon
()
{
return
this
.
isLoadingCollapsedDiff
||
(
!
this
.
file
.
renderIt
&&
!
this
.
isCollapsed
);
return
(
this
.
idState
.
isLoadingCollapsedDiff
||
(
!
this
.
file
.
renderIt
&&
!
this
.
idState
.
isCollapsed
)
);
},
hasDiff
()
{
return
hasDiff
(
this
.
file
);
...
...
@@ -145,13 +148,13 @@ export default {
return
collapsedType
(
this
.
file
)
===
DIFF_FILE_MANUAL_COLLAPSE
;
},
showBody
()
{
return
!
this
.
isCollapsed
||
this
.
automaticallyCollapsed
;
return
!
this
.
i
dState
.
i
sCollapsed
||
this
.
automaticallyCollapsed
;
},
showWarning
()
{
return
this
.
isCollapsed
&&
this
.
automaticallyCollapsed
&&
!
this
.
viewDiffsFileByFile
;
return
this
.
i
dState
.
i
sCollapsed
&&
this
.
automaticallyCollapsed
&&
!
this
.
viewDiffsFileByFile
;
},
showContent
()
{
return
!
this
.
isCollapsed
&&
!
this
.
isFileTooLarge
;
return
!
this
.
i
dState
.
i
sCollapsed
&&
!
this
.
isFileTooLarge
;
},
showLocalFileReviews
()
{
const
loggedIn
=
Boolean
(
gon
.
current_user_id
);
...
...
@@ -179,23 +182,16 @@ export default {
this
.
requestDiff
();
}
},
immediate
:
true
,
},
'
file.viewer.automaticallyCollapsed
'
:
{
handler
:
function
autoChangeWatch
(
automaticValue
)
{
if
(
collapsedType
(
this
.
file
)
!==
DIFF_FILE_MANUAL_COLLAPSE
)
{
this
.
isCollapsed
=
this
.
viewDiffsFileByFile
?
false
:
automaticValue
;
}
this
.
handleAutomaticallyCollapsed
(
automaticValue
);
},
immediate
:
true
,
},
'
file.viewer.manuallyCollapsed
'
:
{
handler
:
function
manualChangeWatch
(
manualValue
)
{
if
(
manualValue
!==
null
)
{
this
.
isCollapsed
=
manualValue
;
}
this
.
handleManualChangeWatch
(
manualValue
);
},
immediate
:
true
,
},
},
created
()
{
...
...
@@ -212,6 +208,8 @@ export default {
}
this
.
manageViewedEffects
();
this
.
handleAutomaticallyCollapsed
(
this
.
file
.
viewer
.
automaticallyCollapsed
);
this
.
handleManualChangeWatch
(
this
.
file
.
viewer
.
manuallyCollapsed
);
},
beforeDestroy
()
{
if
(
this
.
preRender
)
return
;
...
...
@@ -226,12 +224,12 @@ export default {
'
setFileCollapsedByUser
'
,
]),
manageViewedEffects
()
{
if
(
this
.
reviewed
&&
!
this
.
isCollapsed
&&
this
.
showLocalFileReviews
)
{
if
(
this
.
reviewed
&&
!
this
.
i
dState
.
i
sCollapsed
&&
this
.
showLocalFileReviews
)
{
this
.
handleToggle
();
}
},
expandAllListener
()
{
if
(
this
.
isCollapsed
)
{
if
(
this
.
i
dState
.
i
sCollapsed
)
{
this
.
handleToggle
();
}
},
...
...
@@ -253,7 +251,7 @@ export default {
});
},
handleToggle
({
viaUserInteraction
=
false
}
=
{})
{
const
collapsingNow
=
!
this
.
isCollapsed
;
const
collapsingNow
=
!
this
.
i
dState
.
i
sCollapsed
;
const
contentElement
=
this
.
$el
.
querySelector
(
`#diff-content-
${
this
.
file
.
file_hash
}
`
);
this
.
setFileCollapsedByUser
({
...
...
@@ -270,11 +268,11 @@ export default {
}
},
requestDiff
()
{
this
.
isLoadingCollapsedDiff
=
true
;
this
.
i
dState
.
i
sLoadingCollapsedDiff
=
true
;
this
.
loadCollapsedDiff
(
this
.
file
)
.
then
(()
=>
{
this
.
isLoadingCollapsedDiff
=
false
;
this
.
i
dState
.
i
sLoadingCollapsedDiff
=
false
;
this
.
setRenderIt
(
this
.
file
);
})
.
then
(()
=>
{
...
...
@@ -287,17 +285,27 @@ export default {
);
})
.
catch
(()
=>
{
this
.
isLoadingCollapsedDiff
=
false
;
this
.
i
dState
.
i
sLoadingCollapsedDiff
=
false
;
createFlash
({
message
:
this
.
$options
.
i18n
.
genericError
,
});
});
},
showForkMessage
()
{
this
.
forkMessageVisible
=
true
;
this
.
idState
.
forkMessageVisible
=
true
;
},
hideForkMessage
()
{
this
.
forkMessageVisible
=
false
;
this
.
idState
.
forkMessageVisible
=
false
;
},
handleAutomaticallyCollapsed
(
automaticValue
)
{
if
(
collapsedType
(
this
.
file
)
!==
DIFF_FILE_MANUAL_COLLAPSE
)
{
this
.
idState
.
isCollapsed
=
this
.
viewDiffsFileByFile
?
false
:
automaticValue
;
}
},
handleManualChangeWatch
(
manualValue
)
{
if
(
manualValue
!==
null
)
{
this
.
idState
.
isCollapsed
=
manualValue
;
}
},
},
};
...
...
@@ -320,7 +328,7 @@ export default {
:diff-file=
"file"
:collapsible=
"true"
:reviewed=
"reviewed"
:expanded=
"!isCollapsed"
:expanded=
"!i
dState.i
sCollapsed"
:add-merge-request-buttons=
"true"
:view-diffs-file-by-file=
"viewDiffsFileByFile"
:show-local-file-reviews=
"showLocalFileReviews"
...
...
@@ -331,7 +339,10 @@ export default {
@showForkMessage="showForkMessage"
/>
<div
v-if=
"forkMessageVisible"
class=
"js-file-fork-suggestion-section file-fork-suggestion"
>
<div
v-if=
"idState.forkMessageVisible"
class=
"js-file-fork-suggestion-section file-fork-suggestion"
>
<span
v-safe-html=
"forkMessage"
class=
"file-fork-suggestion-note"
></span>
<a
:href=
"file.fork_path"
...
...
app/assets/javascripts/diffs/components/diff_file_header.vue
View file @
008921cd
...
...
@@ -13,6 +13,7 @@ import {
}
from
'
@gitlab/ui
'
;
import
{
escape
}
from
'
lodash
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
IdState
}
from
'
vendor/vue-virtual-scroller
'
;
import
{
diffViewerModes
}
from
'
~/ide/constants
'
;
import
{
scrollToElement
}
from
'
~/lib/utils/common_utils
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
...
...
@@ -47,7 +48,7 @@ export default {
GlTooltip
:
GlTooltipDirective
,
SafeHtml
:
GlSafeHtmlDirective
,
},
mixins
:
[
glFeatureFlagsMixin
()],
mixins
:
[
glFeatureFlagsMixin
()
,
IdState
({
idProp
:
(
vm
)
=>
vm
.
diffFile
.
file_hash
})
],
i18n
:
{
...
DIFF_FILE_HEADER
,
compareButtonLabel
:
s__
(
'
Compare submodule commit revisions
'
),
...
...
@@ -102,7 +103,7 @@ export default {
default
:
()
=>
[],
},
},
data
()
{
idState
()
{
return
{
moreActionsShown
:
false
,
};
...
...
@@ -239,7 +240,7 @@ export default {
}
},
setMoreActionsShown
(
val
)
{
this
.
moreActionsShown
=
val
;
this
.
idState
.
moreActionsShown
=
val
;
},
toggleReview
(
newReviewedStatus
)
{
const
autoCollapsed
=
...
...
@@ -268,7 +269,7 @@ export default {
<
template
>
<div
ref=
"header"
:class=
"
{ 'gl-z-dropdown-menu!': moreActionsShown }"
:class=
"
{ 'gl-z-dropdown-menu!':
idState.
moreActionsShown }"
class="js-file-title file-title file-title-flex-parent"
data-qa-selector="file_title_container"
:data-qa-file-name="filePath"
...
...
app/assets/javascripts/diffs/components/diff_view.vue
View file @
008921cd
<
script
>
import
{
mapGetters
,
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
IdState
}
from
'
vendor/vue-virtual-scroller
'
;
import
DraftNote
from
'
~/batch_comments/components/draft_note.vue
'
;
import
draftCommentsMixin
from
'
~/diffs/mixins/draft_comments
'
;
import
{
getCommentedLines
}
from
'
~/notes/components/multiline_comment_utils
'
;
...
...
@@ -17,7 +18,11 @@ export default {
DiffCommentCell
,
DraftNote
,
},
mixins
:
[
draftCommentsMixin
,
glFeatureFlagsMixin
()],
mixins
:
[
draftCommentsMixin
,
glFeatureFlagsMixin
(),
IdState
({
idProp
:
(
vm
)
=>
vm
.
diffFile
.
file_hash
}),
],
props
:
{
diffFile
:
{
type
:
Object
,
...
...
@@ -38,7 +43,7 @@ export default {
default
:
false
,
},
},
data
()
{
idState
()
{
return
{
dragStart
:
null
,
updatedLineRange
:
null
,
...
...
@@ -80,29 +85,29 @@ export default {
if
(
event
.
target
?.
parentNode
)
{
hide
(
event
.
target
.
parentNode
);
}
this
.
dragStart
=
line
;
this
.
idState
.
dragStart
=
line
;
},
onDragOver
(
line
)
{
if
(
line
.
chunk
!==
this
.
dragStart
.
chunk
)
return
;
if
(
line
.
chunk
!==
this
.
idState
.
dragStart
.
chunk
)
return
;
let
start
=
this
.
dragStart
;
let
start
=
this
.
idState
.
dragStart
;
let
end
=
line
;
if
(
this
.
dragStart
.
index
>=
line
.
index
)
{
if
(
this
.
idState
.
dragStart
.
index
>=
line
.
index
)
{
start
=
line
;
end
=
this
.
dragStart
;
end
=
this
.
idState
.
dragStart
;
}
this
.
updatedLineRange
=
{
start
,
end
};
this
.
idState
.
updatedLineRange
=
{
start
,
end
};
this
.
setSelectedCommentPosition
(
this
.
updatedLineRange
);
this
.
setSelectedCommentPosition
(
this
.
idState
.
updatedLineRange
);
},
onStopDragging
()
{
this
.
showCommentForm
({
lineCode
:
this
.
updatedLineRange
?.
end
?.
line_code
,
lineCode
:
this
.
idState
.
updatedLineRange
?.
end
?.
line_code
,
fileHash
:
this
.
diffFile
.
file_hash
,
});
this
.
dragStart
=
null
;
this
.
idState
.
dragStart
=
null
;
},
isHighlighted
(
line
)
{
return
isHighlighted
(
...
...
app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
View file @
008921cd
...
...
@@ -3,6 +3,7 @@ import { GlDropdownItem } from '@gitlab/ui';
import
{
cloneDeep
}
from
'
lodash
'
;
import
Vue
from
'
vue
'
;
import
createFlash
from
'
~/flash
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
IssuableType
}
from
'
~/issue_show/constants
'
;
import
{
__
,
n__
}
from
'
~/locale
'
;
import
SidebarAssigneesRealtime
from
'
~/sidebar/components/assignees/assignees_realtime.vue
'
;
...
...
@@ -80,6 +81,8 @@ export default {
selected
:
[],
isSettingAssignees
:
false
,
isDirty
:
false
,
oldIid
:
null
,
oldSelected
:
null
,
};
},
apollo
:
{
...
...
@@ -142,6 +145,14 @@ export default {
return
this
.
currentUser
.
username
!==
undefined
;
},
},
watch
:
{
iid
(
_
,
oldIid
)
{
if
(
this
.
isDirty
)
{
this
.
oldIid
=
oldIid
;
this
.
oldSelected
=
this
.
selected
;
}
},
},
created
()
{
assigneesWidget
.
updateAssignees
=
this
.
updateAssignees
;
},
...
...
@@ -157,10 +168,14 @@ export default {
variables
:
{
...
this
.
queryVariables
,
assigneeUsernames
,
iid
:
this
.
oldIid
||
this
.
iid
,
},
})
.
then
(({
data
})
=>
{
this
.
$emit
(
'
assignees-updated
'
,
data
.
issuableSetAssignees
.
issuable
.
assignees
.
nodes
);
this
.
$emit
(
'
assignees-updated
'
,
{
id
:
getIdFromGraphQLId
(
data
.
issuableSetAssignees
.
issuable
.
id
),
assignees
:
data
.
issuableSetAssignees
.
issuable
.
assignees
.
nodes
,
});
return
data
;
})
.
catch
(()
=>
{
...
...
@@ -176,7 +191,10 @@ export default {
saveAssignees
()
{
if
(
this
.
isDirty
)
{
this
.
isDirty
=
false
;
this
.
updateAssignees
(
this
.
selected
.
map
(({
username
})
=>
username
));
const
usernames
=
this
.
oldSelected
||
this
.
selected
;
this
.
updateAssignees
(
usernames
.
map
(({
username
})
=>
username
));
this
.
oldIid
=
null
;
this
.
oldSelected
=
null
;
}
this
.
$el
.
dispatchEvent
(
hideDropdownEvent
);
},
...
...
app/workers/gitlab/import/stuck_import_job.rb
View file @
008921cd
...
...
@@ -5,7 +5,7 @@ module Gitlab
module
StuckImportJob
extend
ActiveSupport
::
Concern
IMPORT_JOBS_EXPIRATION
=
15
.
hours
.
seconds
.
to_i
IMPORT_JOBS_EXPIRATION
=
24
.
hours
.
seconds
.
to_i
included
do
include
ApplicationWorker
...
...
spec/fast_spec_helper.rb
View file @
008921cd
...
...
@@ -25,6 +25,8 @@ ActiveSupport::XmlMini.backend = 'Nokogiri'
RSpec
.
configure
do
|
config
|
unless
ENV
[
'CI'
]
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
config
.
filter_run
focus:
true
config
.
run_all_when_everything_filtered
=
true
end
...
...
spec/frontend/boards/stores/actions_spec.js
View file @
008921cd
...
...
@@ -1111,16 +1111,13 @@ describe('updateIssueOrder', () => {
describe
(
'
setAssignees
'
,
()
=>
{
const
node
=
{
username
:
'
name
'
};
const
projectPath
=
'
h/h
'
;
const
refPath
=
`
${
projectPath
}
#3`
;
const
iid
=
'
1
'
;
describe
(
'
when succeeds
'
,
()
=>
{
it
(
'
calls the correct mutation with the correct values
'
,
(
done
)
=>
{
testAction
(
actions
.
setAssignees
,
[
node
]
,
{
activeBoardItem
:
{
iid
,
referencePath
:
refPath
},
commit
:
()
=>
{}
},
{
assignees
:
[
node
],
iid
:
'
1
'
}
,
{
commit
:
()
=>
{}
},
[
{
type
:
'
UPDATE_BOARD_ITEM_BY_ID
'
,
...
...
spec/frontend/diffs/components/diff_view_spec.js
View file @
008921cd
...
...
@@ -41,7 +41,7 @@ describe('DiffView', () => {
});
const
propsData
=
{
diffFile
:
{},
diffFile
:
{
file_hash
:
'
123
'
},
diffLines
:
[],
...
props
,
};
...
...
@@ -85,7 +85,7 @@ describe('DiffView', () => {
const
wrapper
=
createWrapper
({
diffLines
:
[{}]
});
wrapper
.
findComponent
(
DiffRow
).
vm
.
$emit
(
'
startdragging
'
,
{
line
:
{
test
:
true
}
});
expect
(
wrapper
.
vm
.
dragStart
).
toEqual
({
test
:
true
});
expect
(
wrapper
.
vm
.
idState
.
dragStart
).
toEqual
({
test
:
true
});
});
it
(
'
does not call `setSelectedCommentPosition` on different chunks onDragOver
'
,
()
=>
{
...
...
@@ -123,10 +123,10 @@ describe('DiffView', () => {
const
diffRow
=
getDiffRow
(
wrapper
);
diffRow
.
$emit
(
'
startdragging
'
,
{
line
:
{
test
:
true
}
});
expect
(
wrapper
.
vm
.
dragStart
).
toEqual
({
test
:
true
});
expect
(
wrapper
.
vm
.
idState
.
dragStart
).
toEqual
({
test
:
true
});
diffRow
.
$emit
(
'
stopdragging
'
);
expect
(
wrapper
.
vm
.
dragStart
).
toBeNull
();
expect
(
wrapper
.
vm
.
idState
.
dragStart
).
toBeNull
();
expect
(
showCommentForm
).
toHaveBeenCalled
();
});
});
...
...
spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
View file @
008921cd
...
...
@@ -176,7 +176,7 @@ describe('Sidebar assignees widget', () => {
).
toBe
(
true
);
});
it
(
'
emits an event with assignees list on successful mutation
'
,
async
()
=>
{
it
(
'
emits an event with assignees list
and issuable id
on successful mutation
'
,
async
()
=>
{
createComponent
();
await
waitForPromises
();
...
...
@@ -193,18 +193,21 @@ describe('Sidebar assignees widget', () => {
expect
(
wrapper
.
emitted
(
'
assignees-updated
'
)).
toEqual
([
[
[
{
__typename
:
'
User
'
,
avatarUrl
:
'
https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon
'
,
id
:
'
gid://gitlab/User/1
'
,
name
:
'
Administrator
'
,
username
:
'
root
'
,
webUrl
:
'
/root
'
,
status
:
null
,
},
],
{
assignees
:
[
{
__typename
:
'
User
'
,
avatarUrl
:
'
https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon
'
,
id
:
'
gid://gitlab/User/1
'
,
name
:
'
Administrator
'
,
username
:
'
root
'
,
webUrl
:
'
/root
'
,
status
:
null
,
},
],
id
:
1
,
},
],
]);
});
...
...
@@ -285,6 +288,21 @@ describe('Sidebar assignees widget', () => {
expect
(
updateIssueAssigneesMutationSuccess
).
not
.
toHaveBeenCalled
();
expect
(
findUserSelect
().
isVisible
()).
toBe
(
true
);
});
it
(
'
calls the mutation old issuable id if `iid` prop was changed
'
,
async
()
=>
{
findUserSelect
().
vm
.
$emit
(
'
input
'
,
[{
username
:
'
francina.skiles
'
}]);
wrapper
.
setProps
({
iid
:
'
2
'
,
});
await
nextTick
();
findEditableItem
().
vm
.
$emit
(
'
close
'
);
expect
(
updateIssueAssigneesMutationSuccess
).
toHaveBeenCalledWith
({
assigneeUsernames
:
[
'
francina.skiles
'
],
fullPath
:
'
/mygroup/myProject
'
,
iid
:
'
1
'
,
});
});
});
it
(
'
shows an error if update assignees mutation is rejected
'
,
async
()
=>
{
...
...
spec/spec_helper.rb
View file @
008921cd
...
...
@@ -110,8 +110,11 @@ RSpec.configure do |config|
end
unless
ENV
[
'CI'
]
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
config
.
filter_run
focus:
true
config
.
run_all_when_everything_filtered
=
true
# Re-run failures locally with `--only-failures`
config
.
example_status_persistence_file_path
=
'./spec/examples.txt'
end
...
...
workhorse/internal/api/api.go
View file @
008921cd
...
...
@@ -3,6 +3,7 @@ package api
import
(
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
...
...
@@ -29,6 +30,8 @@ const (
ResponseContentType
=
"application/vnd.gitlab-workhorse+json"
failureResponseLimit
=
32768
geoProxyEndpointPath
=
"/api/v4/geo/proxy"
)
type
API
struct
{
...
...
@@ -37,6 +40,8 @@ type API struct {
Version
string
}
var
ErrNotGeoSecondary
=
errors
.
New
(
"this is not a Geo secondary site"
)
var
(
requestsCounter
=
promauto
.
NewCounterVec
(
prometheus
.
CounterOpts
{
...
...
@@ -61,6 +66,10 @@ func NewAPI(myURL *url.URL, version string, roundTripper http.RoundTripper) *API
}
}
type
GeoProxyEndpointResponse
struct
{
GeoProxyURL
string
`json:"geo_proxy_url"`
}
type
HandleFunc
func
(
http
.
ResponseWriter
,
*
http
.
Request
,
*
Response
)
type
MultipartUploadParams
struct
{
...
...
@@ -389,3 +398,40 @@ func bufferResponse(r io.Reader) (*bytes.Buffer, error) {
func
validResponseContentType
(
resp
*
http
.
Response
)
bool
{
return
helper
.
IsContentType
(
ResponseContentType
,
resp
.
Header
.
Get
(
"Content-Type"
))
}
// TODO: Cache the result of the API requests https://gitlab.com/gitlab-org/gitlab/-/issues/329671
func
(
api
*
API
)
GetGeoProxyURL
()
(
*
url
.
URL
,
error
)
{
geoProxyApiUrl
:=
*
api
.
URL
geoProxyApiUrl
.
Path
,
geoProxyApiUrl
.
RawPath
=
joinURLPath
(
api
.
URL
,
geoProxyEndpointPath
)
geoProxyApiReq
:=
&
http
.
Request
{
Method
:
"GET"
,
URL
:
&
geoProxyApiUrl
,
Header
:
make
(
http
.
Header
),
}
httpResponse
,
err
:=
api
.
doRequestWithoutRedirects
(
geoProxyApiReq
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"GetGeoProxyURL: do request: %v"
,
err
)
}
defer
httpResponse
.
Body
.
Close
()
if
httpResponse
.
StatusCode
!=
http
.
StatusOK
{
return
nil
,
fmt
.
Errorf
(
"GetGeoProxyURL: Received HTTP status code: %v"
,
httpResponse
.
StatusCode
)
}
response
:=
&
GeoProxyEndpointResponse
{}
if
err
:=
json
.
NewDecoder
(
httpResponse
.
Body
)
.
Decode
(
response
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"GetGeoProxyURL: decode response: %v"
,
err
)
}
if
response
.
GeoProxyURL
==
""
{
return
nil
,
ErrNotGeoSecondary
}
geoProxyURL
,
err
:=
url
.
Parse
(
response
.
GeoProxyURL
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"GetGeoProxyURL: Could not parse Geo proxy URL: %v, err: %v"
,
response
.
GeoProxyURL
,
err
)
}
return
geoProxyURL
,
nil
}
workhorse/internal/api/api_test.go
0 → 100644
View file @
008921cd
package
api
import
(
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"regexp"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
)
func
TestGetGeoProxyURLWhenGeoSecondary
(
t
*
testing
.
T
)
{
geoProxyURL
,
err
:=
getGeoProxyURLGivenResponse
(
t
,
`{"geo_proxy_url":"http://primary"}`
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
geoProxyURL
)
require
.
Equal
(
t
,
"http://primary"
,
geoProxyURL
.
String
())
}
func
TestGetGeoProxyURLWhenGeoPrimaryOrNonGeo
(
t
*
testing
.
T
)
{
geoProxyURL
,
err
:=
getGeoProxyURLGivenResponse
(
t
,
"{}"
)
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
ErrNotGeoSecondary
,
err
)
require
.
Nil
(
t
,
geoProxyURL
)
}
func
getGeoProxyURLGivenResponse
(
t
*
testing
.
T
,
givenInternalApiResponse
string
)
(
*
url
.
URL
,
error
)
{
t
.
Helper
()
ts
:=
testRailsServer
(
regexp
.
MustCompile
(
`/api/v4/geo/proxy`
),
200
,
givenInternalApiResponse
)
defer
ts
.
Close
()
backend
:=
helper
.
URLMustParse
(
ts
.
URL
)
version
:=
"123"
rt
:=
roundtripper
.
NewTestBackendRoundTripper
(
backend
)
testhelper
.
ConfigureSecret
()
apiClient
:=
NewAPI
(
backend
,
version
,
rt
)
geoProxyURL
,
err
:=
apiClient
.
GetGeoProxyURL
()
return
geoProxyURL
,
err
}
func
testRailsServer
(
url
*
regexp
.
Regexp
,
code
int
,
body
string
)
*
httptest
.
Server
{
return
testhelper
.
TestServerWithHandler
(
url
,
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
// return a 204 No Content response if we don't receive the JWT header
if
r
.
Header
.
Get
(
secret
.
RequestHeader
)
==
""
{
w
.
WriteHeader
(
204
)
return
}
w
.
Header
()
.
Set
(
"Content-Type"
,
ResponseContentType
)
logEntry
:=
log
.
WithFields
(
log
.
Fields
{
"method"
:
r
.
Method
,
"url"
:
r
.
URL
,
})
logEntryWithCode
:=
logEntry
.
WithField
(
"code"
,
code
)
// Write pure string
logEntryWithCode
.
Info
(
"UPSTREAM"
)
w
.
WriteHeader
(
code
)
fmt
.
Fprint
(
w
,
body
)
})
}
workhorse/internal/upstream/routes.go
View file @
008921cd
...
...
@@ -191,12 +191,7 @@ func buildProxy(backend *url.URL, version string, rt http.RoundTripper, cfg conf
// see upstream.ServeHTTP
func
configureRoutes
(
u
*
upstream
)
{
api
:=
apipkg
.
NewAPI
(
u
.
Backend
,
u
.
Version
,
u
.
RoundTripper
,
)
api
:=
u
.
APIClient
static
:=
&
staticpages
.
Static
{
DocumentRoot
:
u
.
DocumentRoot
,
Exclude
:
staticExclude
}
proxy
:=
buildProxy
(
u
.
Backend
,
u
.
Version
,
u
.
RoundTripper
,
u
.
Config
)
cableProxy
:=
proxypkg
.
NewProxy
(
u
.
CableBackend
,
u
.
Version
,
u
.
CableRoundTripper
)
...
...
workhorse/internal/upstream/upstream.go
View file @
008921cd
...
...
@@ -8,6 +8,7 @@ package upstream
import
(
"fmt"
"os"
"net/http"
"strings"
...
...
@@ -15,8 +16,10 @@ import (
"github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/labkit/correlation"
apipkg
"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/rejectmethods"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
...
...
@@ -32,11 +35,13 @@ var (
type
upstream
struct
{
config
.
Config
URLPrefix
urlprefix
.
Prefix
Routes
[]
routeEntry
RoundTripper
http
.
RoundTripper
CableRoundTripper
http
.
RoundTripper
accessLogger
*
logrus
.
Logger
URLPrefix
urlprefix
.
Prefix
Routes
[]
routeEntry
RoundTripper
http
.
RoundTripper
CableRoundTripper
http
.
RoundTripper
APIClient
*
apipkg
.
API
accessLogger
*
logrus
.
Logger
enableGeoProxyFeature
bool
}
func
NewUpstream
(
cfg
config
.
Config
,
accessLogger
*
logrus
.
Logger
)
http
.
Handler
{
...
...
@@ -60,6 +65,13 @@ func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback
up
.
RoundTripper
=
roundtripper
.
NewBackendRoundTripper
(
up
.
Backend
,
up
.
Socket
,
up
.
ProxyHeadersTimeout
,
cfg
.
DevelopmentMode
)
up
.
CableRoundTripper
=
roundtripper
.
NewBackendRoundTripper
(
up
.
CableBackend
,
up
.
CableSocket
,
up
.
ProxyHeadersTimeout
,
cfg
.
DevelopmentMode
)
up
.
configureURLPrefix
()
up
.
APIClient
=
apipkg
.
NewAPI
(
up
.
Backend
,
up
.
Version
,
up
.
RoundTripper
,
)
// Kind of a feature flag. See https://gitlab.com/groups/gitlab-org/-/epics/5914#note_564974130
up
.
enableGeoProxyFeature
=
os
.
Getenv
(
"GEO_SECONDARY_PROXY"
)
==
"1"
routesCallback
(
&
up
)
var
correlationOpts
[]
correlation
.
InboundHandlerOption
...
...
@@ -108,6 +120,17 @@ func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Look for a matching route
var
route
*
routeEntry
if
u
.
enableGeoProxyFeature
{
geoProxyURL
,
err
:=
u
.
APIClient
.
GetGeoProxyURL
()
if
err
==
nil
{
log
.
WithRequest
(
r
)
.
WithFields
(
log
.
Fields
{
"geoProxyURL"
:
geoProxyURL
})
.
Info
(
"Geo Proxy: Set route according to Geo Proxy logic"
)
}
else
if
err
!=
apipkg
.
ErrNotGeoSecondary
{
log
.
WithRequest
(
r
)
.
WithError
(
err
)
.
Error
(
"Geo Proxy: Unable to determine Geo Proxy URL. Falling back to normal routing"
)
}
}
for
_
,
ro
:=
range
u
.
Routes
{
if
ro
.
isMatch
(
prefix
.
Strip
(
URIPath
),
r
)
{
route
=
&
ro
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment