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
0
Merge Requests
0
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
Léo-Paul Géneau
gitlab-ce
Commits
7a66cd68
Commit
7a66cd68
authored
May 04, 2017
by
Filipa Lacerda
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'deploy-keys-load-async' into 'master'
Deploy keys load async Closes #29667 See merge request !10973
parents
97a9a38b
136baeda
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
961 additions
and
37 deletions
+961
-37
app/assets/javascripts/deploy_keys/components/action_btn.vue
app/assets/javascripts/deploy_keys/components/action_btn.vue
+54
-0
app/assets/javascripts/deploy_keys/components/app.vue
app/assets/javascripts/deploy_keys/components/app.vue
+102
-0
app/assets/javascripts/deploy_keys/components/key.vue
app/assets/javascripts/deploy_keys/components/key.vue
+80
-0
app/assets/javascripts/deploy_keys/components/keys_panel.vue
app/assets/javascripts/deploy_keys/components/keys_panel.vue
+52
-0
app/assets/javascripts/deploy_keys/eventhub.js
app/assets/javascripts/deploy_keys/eventhub.js
+3
-0
app/assets/javascripts/deploy_keys/index.js
app/assets/javascripts/deploy_keys/index.js
+21
-0
app/assets/javascripts/deploy_keys/service/index.js
app/assets/javascripts/deploy_keys/service/index.js
+34
-0
app/assets/javascripts/deploy_keys/store/index.js
app/assets/javascripts/deploy_keys/store/index.js
+9
-0
app/controllers/projects/deploy_keys_controller.rb
app/controllers/projects/deploy_keys_controller.rb
+16
-4
app/presenters/projects/settings/deploy_keys_presenter.rb
app/presenters/projects/settings/deploy_keys_presenter.rb
+11
-0
app/serializers/deploy_key_entity.rb
app/serializers/deploy_key_entity.rb
+14
-0
app/serializers/deploy_key_serializer.rb
app/serializers/deploy_key_serializer.rb
+3
-0
app/serializers/project_entity.rb
app/serializers/project_entity.rb
+14
-0
app/views/projects/deploy_keys/_index.html.haml
app/views/projects/deploy_keys/_index.html.haml
+1
-22
app/views/projects/settings/repository/show.html.haml
app/views/projects/settings/repository/show.html.haml
+4
-0
changelogs/unreleased/deploy-keys-load-async.yml
changelogs/unreleased/deploy-keys-load-async.yml
+4
-0
config/webpack.config.js
config/webpack.config.js
+2
-0
features/project/deploy_keys.feature
features/project/deploy_keys.feature
+6
-0
features/steps/project/deploy_keys.rb
features/steps/project/deploy_keys.rb
+9
-7
spec/controllers/projects/deploy_keys_controller_spec.rb
spec/controllers/projects/deploy_keys_controller_spec.rb
+66
-0
spec/features/projects/deploy_keys_spec.rb
spec/features/projects/deploy_keys_spec.rb
+8
-4
spec/javascripts/deploy_keys/components/action_btn_spec.js
spec/javascripts/deploy_keys/components/action_btn_spec.js
+70
-0
spec/javascripts/deploy_keys/components/app_spec.js
spec/javascripts/deploy_keys/components/app_spec.js
+142
-0
spec/javascripts/deploy_keys/components/key_spec.js
spec/javascripts/deploy_keys/components/key_spec.js
+92
-0
spec/javascripts/deploy_keys/components/keys_panel_spec.js
spec/javascripts/deploy_keys/components/keys_panel_spec.js
+70
-0
spec/javascripts/fixtures/deploy_keys.rb
spec/javascripts/fixtures/deploy_keys.rb
+36
-0
spec/serializers/deploy_key_entity_spec.rb
spec/serializers/deploy_key_entity_spec.rb
+38
-0
No files found.
app/assets/javascripts/deploy_keys/components/action_btn.vue
0 → 100644
View file @
7a66cd68
<
script
>
import
eventHub
from
'
../eventhub
'
;
export
default
{
data
()
{
return
{
isLoading
:
false
,
};
},
props
:
{
deployKey
:
{
type
:
Object
,
required
:
true
,
},
type
:
{
type
:
String
,
required
:
true
,
},
btnCssClass
:
{
type
:
String
,
required
:
false
,
default
:
'
btn-default
'
,
},
},
methods
:
{
doAction
()
{
this
.
isLoading
=
true
;
eventHub
.
$emit
(
`
${
this
.
type
}
.key`
,
this
.
deployKey
);
},
},
computed
:
{
text
()
{
return
`
${
this
.
type
.
charAt
(
0
).
toUpperCase
()}${
this
.
type
.
slice
(
1
)}
`
;
},
},
};
</
script
>
<
template
>
<button
class=
"btn btn-sm prepend-left-10"
:class=
"[
{ disabled: isLoading }, btnCssClass]"
:disabled="isLoading"
@click="doAction">
{{
text
}}
<i
v-if=
"isLoading"
class=
"fa fa-spinner fa-spin"
aria-hidden=
"true"
aria-label=
"Loading"
>
</i>
</button>
</
template
>
app/assets/javascripts/deploy_keys/components/app.vue
0 → 100644
View file @
7a66cd68
<
script
>
/* global Flash */
import
eventHub
from
'
../eventhub
'
;
import
DeployKeysService
from
'
../service
'
;
import
DeployKeysStore
from
'
../store
'
;
import
keysPanel
from
'
./keys_panel.vue
'
;
export
default
{
data
()
{
return
{
isLoading
:
false
,
store
:
new
DeployKeysStore
(),
};
},
props
:
{
endpoint
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
hasKeys
()
{
return
Object
.
keys
(
this
.
keys
).
length
;
},
keys
()
{
return
this
.
store
.
keys
;
},
},
components
:
{
keysPanel
,
},
methods
:
{
fetchKeys
()
{
this
.
isLoading
=
true
;
this
.
service
.
getKeys
()
.
then
((
data
)
=>
{
this
.
isLoading
=
false
;
this
.
store
.
keys
=
data
;
})
.
catch
(()
=>
new
Flash
(
'
Error getting deploy keys
'
));
},
enableKey
(
deployKey
)
{
this
.
service
.
enableKey
(
deployKey
.
id
)
.
then
(()
=>
this
.
fetchKeys
())
.
catch
(()
=>
new
Flash
(
'
Error enabling deploy key
'
));
},
disableKey
(
deployKey
)
{
// eslint-disable-next-line no-alert
if
(
confirm
(
'
You are going to remove this deploy key. Are you sure?
'
))
{
this
.
service
.
disableKey
(
deployKey
.
id
)
.
then
(()
=>
this
.
fetchKeys
())
.
catch
(()
=>
new
Flash
(
'
Error removing deploy key
'
));
}
},
},
created
()
{
this
.
service
=
new
DeployKeysService
(
this
.
endpoint
);
eventHub
.
$on
(
'
enable.key
'
,
this
.
enableKey
);
eventHub
.
$on
(
'
remove.key
'
,
this
.
disableKey
);
eventHub
.
$on
(
'
disable.key
'
,
this
.
disableKey
);
},
mounted
()
{
this
.
fetchKeys
();
},
beforeDestroy
()
{
eventHub
.
$off
(
'
enable.key
'
,
this
.
enableKey
);
eventHub
.
$off
(
'
remove.key
'
,
this
.
disableKey
);
eventHub
.
$off
(
'
disable.key
'
,
this
.
disableKey
);
},
};
</
script
>
<
template
>
<div
class=
"col-lg-9 col-lg-offset-3 append-bottom-default deploy-keys"
>
<div
class=
"text-center"
v-if=
"isLoading && !hasKeys"
>
<i
class=
"fa fa-spinner fa-spin fa-2x"
aria-hidden=
"true"
aria-label=
"Loading deploy keys"
>
</i>
</div>
<div
v-else-if=
"hasKeys"
>
<keys-panel
title=
"Enabled deploy keys for this project"
:keys=
"keys.enabled_keys"
:store=
"store"
/>
<keys-panel
title=
"Deploy keys from projects you have access to"
:keys=
"keys.available_project_keys"
:store=
"store"
/>
<keys-panel
v-if=
"keys.public_keys.length"
title=
"Public deploy keys available to any project"
:keys=
"keys.public_keys"
:store=
"store"
/>
</div>
</div>
</
template
>
app/assets/javascripts/deploy_keys/components/key.vue
0 → 100644
View file @
7a66cd68
<
script
>
import
actionBtn
from
'
./action_btn.vue
'
;
export
default
{
props
:
{
deployKey
:
{
type
:
Object
,
required
:
true
,
},
store
:
{
type
:
Object
,
required
:
true
,
},
},
components
:
{
actionBtn
,
},
computed
:
{
timeagoDate
()
{
return
gl
.
utils
.
getTimeago
().
format
(
this
.
deployKey
.
created_at
);
},
},
methods
:
{
isEnabled
(
id
)
{
return
this
.
store
.
findEnabledKey
(
id
)
!==
undefined
;
},
},
};
</
script
>
<
template
>
<div>
<div
class=
"pull-left append-right-10 hidden-xs"
>
<i
aria-hidden=
"true"
class=
"fa fa-key key-icon"
>
</i>
</div>
<div
class=
"deploy-key-content key-list-item-info"
>
<strong
class=
"title"
>
{{
deployKey
.
title
}}
</strong>
<div
class=
"description"
>
{{
deployKey
.
fingerprint
}}
</div>
<div
v-if=
"deployKey.can_push"
class=
"write-access-allowed"
>
Write access allowed
</div>
</div>
<div
class=
"deploy-key-content prepend-left-default deploy-key-projects"
>
<a
v-for=
"project in deployKey.projects"
class=
"label deploy-project-label"
:href=
"project.full_path"
>
{{
project
.
full_name
}}
</a>
</div>
<div
class=
"deploy-key-content"
>
<span
class=
"key-created-at"
>
created
{{
timeagoDate
}}
</span>
<action-btn
v-if=
"!isEnabled(deployKey.id)"
:deploy-key=
"deployKey"
type=
"enable"
/>
<action-btn
v-else-if=
"deployKey.destroyed_when_orphaned && deployKey.almost_orphaned"
:deploy-key=
"deployKey"
btn-css-class=
"btn-warning"
type=
"remove"
/>
<action-btn
v-else
:deploy-key=
"deployKey"
btn-css-class=
"btn-warning"
type=
"disable"
/>
</div>
</div>
</
template
>
app/assets/javascripts/deploy_keys/components/keys_panel.vue
0 → 100644
View file @
7a66cd68
<
script
>
import
key
from
'
./key.vue
'
;
export
default
{
props
:
{
title
:
{
type
:
String
,
required
:
true
,
},
keys
:
{
type
:
Array
,
required
:
true
,
},
showHelpBox
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
store
:
{
type
:
Object
,
required
:
true
,
},
},
components
:
{
key
,
},
};
</
script
>
<
template
>
<div
class=
"deploy-keys-panel"
>
<h5>
{{
title
}}
(
{{
keys
.
length
}}
)
</h5>
<ul
class=
"well-list"
v-if=
"keys.length"
>
<li
v-for=
"deployKey in keys"
:key=
"deployKey.id"
>
<key
:deploy-key=
"deployKey"
:store=
"store"
/>
</li>
</ul>
<div
class=
"settings-message text-center"
v-else-if=
"showHelpBox"
>
No deploy keys found. Create one with the form above.
</div>
</div>
</
template
>
app/assets/javascripts/deploy_keys/eventhub.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
export
default
new
Vue
();
app/assets/javascripts/deploy_keys/index.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
deployKeysApp
from
'
./components/app.vue
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
new
Vue
({
el
:
document
.
getElementById
(
'
js-deploy-keys
'
),
data
()
{
return
{
endpoint
:
this
.
$options
.
el
.
dataset
.
endpoint
,
};
},
components
:
{
deployKeysApp
,
},
render
(
createElement
)
{
return
createElement
(
'
deploy-keys-app
'
,
{
props
:
{
endpoint
:
this
.
endpoint
,
},
});
},
}));
app/assets/javascripts/deploy_keys/service/index.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
VueResource
from
'
vue-resource
'
;
Vue
.
use
(
VueResource
);
export
default
class
DeployKeysService
{
constructor
(
endpoint
)
{
this
.
endpoint
=
endpoint
;
this
.
resource
=
Vue
.
resource
(
`
${
this
.
endpoint
}
{/id}`
,
{},
{
enable
:
{
method
:
'
PUT
'
,
url
:
`
${
this
.
endpoint
}
{/id}/enable`
,
},
disable
:
{
method
:
'
PUT
'
,
url
:
`
${
this
.
endpoint
}
{/id}/disable`
,
},
});
}
getKeys
()
{
return
this
.
resource
.
get
()
.
then
(
response
=>
response
.
json
());
}
enableKey
(
id
)
{
return
this
.
resource
.
enable
({
id
},
{});
}
disableKey
(
id
)
{
return
this
.
resource
.
disable
({
id
},
{});
}
}
app/assets/javascripts/deploy_keys/store/index.js
0 → 100644
View file @
7a66cd68
export
default
class
DeployKeysStore
{
constructor
()
{
this
.
keys
=
{};
}
findEnabledKey
(
id
)
{
return
this
.
keys
.
enabled_keys
.
find
(
key
=>
key
.
id
===
id
);
}
}
app/controllers/projects/deploy_keys_controller.rb
View file @
7a66cd68
...
@@ -8,7 +8,12 @@ class Projects::DeployKeysController < Projects::ApplicationController
...
@@ -8,7 +8,12 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout
"project_settings"
layout
"project_settings"
def
index
def
index
redirect_to_repository_settings
(
@project
)
respond_to
do
|
format
|
format
.
html
{
redirect_to_repository_settings
(
@project
)
}
format
.
json
do
render
json:
Projects
::
Settings
::
DeployKeysPresenter
.
new
(
@project
,
current_user:
current_user
).
as_json
end
end
end
end
def
new
def
new
...
@@ -19,7 +24,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
...
@@ -19,7 +24,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
@key
=
DeployKey
.
new
(
deploy_key_params
.
merge
(
user:
current_user
))
@key
=
DeployKey
.
new
(
deploy_key_params
.
merge
(
user:
current_user
))
unless
@key
.
valid?
&&
@project
.
deploy_keys
<<
@key
unless
@key
.
valid?
&&
@project
.
deploy_keys
<<
@key
flash
[
:alert
]
=
@key
.
errors
.
full_messages
.
join
(
', '
).
html_safe
flash
[
:alert
]
=
@key
.
errors
.
full_messages
.
join
(
', '
).
html_safe
end
end
redirect_to_repository_settings
(
@project
)
redirect_to_repository_settings
(
@project
)
end
end
...
@@ -27,7 +32,10 @@ class Projects::DeployKeysController < Projects::ApplicationController
...
@@ -27,7 +32,10 @@ class Projects::DeployKeysController < Projects::ApplicationController
def
enable
def
enable
Projects
::
EnableDeployKeyService
.
new
(
@project
,
current_user
,
params
).
execute
Projects
::
EnableDeployKeyService
.
new
(
@project
,
current_user
,
params
).
execute
redirect_to_repository_settings
(
@project
)
respond_to
do
|
format
|
format
.
html
{
redirect_to_repository_settings
(
@project
)
}
format
.
json
{
head
:ok
}
end
end
end
def
disable
def
disable
...
@@ -35,7 +43,11 @@ class Projects::DeployKeysController < Projects::ApplicationController
...
@@ -35,7 +43,11 @@ class Projects::DeployKeysController < Projects::ApplicationController
return
render_404
unless
deploy_key_project
return
render_404
unless
deploy_key_project
deploy_key_project
.
destroy!
deploy_key_project
.
destroy!
redirect_to_repository_settings
(
@project
)
respond_to
do
|
format
|
format
.
html
{
redirect_to_repository_settings
(
@project
)
}
format
.
json
{
head
:ok
}
end
end
end
protected
protected
...
...
app/presenters/projects/settings/deploy_keys_presenter.rb
View file @
7a66cd68
...
@@ -48,6 +48,17 @@ module Projects
...
@@ -48,6 +48,17 @@ module Projects
available_public_keys
.
any?
available_public_keys
.
any?
end
end
def
as_json
serializer
=
DeployKeySerializer
.
new
opts
=
{
user:
current_user
}
{
enabled_keys:
serializer
.
represent
(
enabled_keys
,
opts
),
available_project_keys:
serializer
.
represent
(
available_project_keys
,
opts
),
public_keys:
serializer
.
represent
(
available_public_keys
,
opts
)
}
end
def
to_partial_path
def
to_partial_path
'projects/deploy_keys/index'
'projects/deploy_keys/index'
end
end
...
...
app/serializers/deploy_key_entity.rb
0 → 100644
View file @
7a66cd68
class
DeployKeyEntity
<
Grape
::
Entity
expose
:id
expose
:user_id
expose
:title
expose
:fingerprint
expose
:can_push
expose
:destroyed_when_orphaned?
,
as: :destroyed_when_orphaned
expose
:almost_orphaned?
,
as: :almost_orphaned
expose
:created_at
expose
:updated_at
expose
:projects
,
using:
ProjectEntity
do
|
deploy_key
|
deploy_key
.
projects
.
select
{
|
project
|
options
[
:user
].
can?
(
:read_project
,
project
)
}
end
end
app/serializers/deploy_key_serializer.rb
0 → 100644
View file @
7a66cd68
class
DeployKeySerializer
<
BaseSerializer
entity
DeployKeyEntity
end
app/serializers/project_entity.rb
0 → 100644
View file @
7a66cd68
class
ProjectEntity
<
Grape
::
Entity
include
RequestAwareEntity
expose
:id
expose
:name
expose
:full_path
do
|
project
|
namespace_project_path
(
project
.
namespace
,
project
)
end
expose
:full_name
do
|
project
|
project
.
full_name
end
end
app/views/projects/deploy_keys/_index.html.haml
View file @
7a66cd68
...
@@ -10,25 +10,4 @@
...
@@ -10,25 +10,4 @@
=
render
@deploy_keys
.
form_partial_path
=
render
@deploy_keys
.
form_partial_path
.col-lg-9.col-lg-offset-3
.col-lg-9.col-lg-offset-3
%hr
%hr
.col-lg-9.col-lg-offset-3.append-bottom-default.deploy-keys
#js-deploy-keys
{
data:
{
endpoint:
namespace_project_deploy_keys_path
}
}
%h5
.prepend-top-0
Enabled deploy keys for this project (
#{
@deploy_keys
.
enabled_keys_size
}
)
-
if
@deploy_keys
.
any_keys_enabled?
%ul
.well-list
=
render
partial:
'projects/deploy_keys/deploy_key'
,
collection:
@deploy_keys
.
enabled_keys
,
as: :deploy_key
-
else
.settings-message.text-center
No deploy keys found. Create one with the form above.
%h5
.prepend-top-default
Deploy keys from projects you have access to (
#{
@deploy_keys
.
available_project_keys_size
}
)
-
if
@deploy_keys
.
any_available_project_keys_enabled?
%ul
.well-list
=
render
partial:
'projects/deploy_keys/deploy_key'
,
collection:
@deploy_keys
.
available_project_keys
,
as: :deploy_key
-
else
.settings-message.text-center
No deploy keys from your projects could be found. Create one with the form above or add existing one below.
-
if
@deploy_keys
.
any_available_public_keys_enabled?
%h5
.prepend-top-default
Public deploy keys available to any project (
#{
@deploy_keys
.
available_public_keys_size
}
)
%ul
.well-list
=
render
partial:
'projects/deploy_keys/deploy_key'
,
collection:
@deploy_keys
.
available_public_keys
,
as: :deploy_key
app/views/projects/settings/repository/show.html.haml
View file @
7a66cd68
-
page_title
"Repository"
-
page_title
"Repository"
=
render
"projects/settings/head"
=
render
"projects/settings/head"
-
content_for
:page_specific_javascripts
do
=
page_specific_javascript_bundle_tag
(
'common_vue'
)
=
page_specific_javascript_bundle_tag
(
'deploy_keys'
)
=
render
@deploy_keys
=
render
@deploy_keys
=
render
"projects/protected_branches/index"
=
render
"projects/protected_branches/index"
=
render
"projects/protected_tags/index"
=
render
"projects/protected_tags/index"
changelogs/unreleased/deploy-keys-load-async.yml
0 → 100644
View file @
7a66cd68
---
title
:
Deploy keys load are loaded async
merge_request
:
author
:
config/webpack.config.js
View file @
7a66cd68
...
@@ -26,6 +26,7 @@ var config = {
...
@@ -26,6 +26,7 @@ var config = {
common_d3
:
[
'
d3
'
],
common_d3
:
[
'
d3
'
],
cycle_analytics
:
'
./cycle_analytics/cycle_analytics_bundle.js
'
,
cycle_analytics
:
'
./cycle_analytics/cycle_analytics_bundle.js
'
,
commit_pipelines
:
'
./commit/pipelines/pipelines_bundle.js
'
,
commit_pipelines
:
'
./commit/pipelines/pipelines_bundle.js
'
,
deploy_keys
:
'
./deploy_keys/index.js
'
,
diff_notes
:
'
./diff_notes/diff_notes_bundle.js
'
,
diff_notes
:
'
./diff_notes/diff_notes_bundle.js
'
,
environments
:
'
./environments/environments_bundle.js
'
,
environments
:
'
./environments/environments_bundle.js
'
,
environments_folder
:
'
./environments/folder/environments_folder_bundle.js
'
,
environments_folder
:
'
./environments/folder/environments_folder_bundle.js
'
,
...
@@ -122,6 +123,7 @@ var config = {
...
@@ -122,6 +123,7 @@ var config = {
'
boards
'
,
'
boards
'
,
'
commit_pipelines
'
,
'
commit_pipelines
'
,
'
cycle_analytics
'
,
'
cycle_analytics
'
,
'
deploy_keys
'
,
'
diff_notes
'
,
'
diff_notes
'
,
'
environments
'
,
'
environments
'
,
'
environments_folder
'
,
'
environments_folder
'
,
...
...
features/project/deploy_keys.feature
View file @
7a66cd68
...
@@ -3,28 +3,33 @@ Feature: Project Deploy Keys
...
@@ -3,28 +3,33 @@ Feature: Project Deploy Keys
Given
I sign in as a user
Given
I sign in as a user
And
I own project
"Shop"
And
I own project
"Shop"
@javascript
Scenario
:
I
should see deploy keys list
Scenario
:
I
should see deploy keys list
Given
project has deploy key
Given
project has deploy key
When
I visit project deploy keys page
When
I visit project deploy keys page
Then
I should see project deploy key
Then
I should see project deploy key
@javascript
Scenario
:
I
should see project deploy keys
Scenario
:
I
should see project deploy keys
Given
other projects have deploy keys
Given
other projects have deploy keys
When
I visit project deploy keys page
When
I visit project deploy keys page
Then
I should see other project deploy key
Then
I should see other project deploy key
And
I should only see the same deploy key once
And
I should only see the same deploy key once
@javascript
Scenario
:
I
should see public deploy keys
Scenario
:
I
should see public deploy keys
Given
public deploy key exists
Given
public deploy key exists
When
I visit project deploy keys page
When
I visit project deploy keys page
Then
I should see public deploy key
Then
I should see public deploy key
@javascript
Scenario
:
I
add new deploy key
Scenario
:
I
add new deploy key
Given
I visit project deploy keys page
Given
I visit project deploy keys page
And
I submit new deploy key
And
I submit new deploy key
Then
I should be on deploy keys page
Then
I should be on deploy keys page
And
I should see newly created deploy key
And
I should see newly created deploy key
@javascript
Scenario
:
I
attach other project deploy key to project
Scenario
:
I
attach other project deploy key to project
Given
other projects have deploy keys
Given
other projects have deploy keys
And
I visit project deploy keys page
And
I visit project deploy keys page
...
@@ -32,6 +37,7 @@ Feature: Project Deploy Keys
...
@@ -32,6 +37,7 @@ Feature: Project Deploy Keys
Then
I should be on deploy keys page
Then
I should be on deploy keys page
And
I should see newly created deploy key
And
I should see newly created deploy key
@javascript
Scenario
:
I
attach public deploy key to project
Scenario
:
I
attach public deploy key to project
Given
public deploy key exists
Given
public deploy key exists
And
I visit project deploy keys page
And
I visit project deploy keys page
...
...
features/steps/project/deploy_keys.rb
View file @
7a66cd68
...
@@ -8,19 +8,19 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
...
@@ -8,19 +8,19 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
end
step
'I should see project deploy key'
do
step
'I should see project deploy key'
do
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
(
page
).
to
have_content
deploy_key
.
title
expect
(
page
).
to
have_content
deploy_key
.
title
end
end
end
end
step
'I should see other project deploy key'
do
step
'I should see other project deploy key'
do
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
(
page
).
to
have_content
other_deploy_key
.
title
expect
(
page
).
to
have_content
other_deploy_key
.
title
end
end
end
end
step
'I should see public deploy key'
do
step
'I should see public deploy key'
do
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
(
page
).
to
have_content
public_deploy_key
.
title
expect
(
page
).
to
have_content
public_deploy_key
.
title
end
end
end
end
...
@@ -40,7 +40,8 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
...
@@ -40,7 +40,8 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
end
step
'I should see newly created deploy key'
do
step
'I should see newly created deploy key'
do
page
.
within
'.deploy-keys'
do
@project
.
reload
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
(
page
).
to
have_content
(
deploy_key
.
title
)
expect
(
page
).
to
have_content
(
deploy_key
.
title
)
end
end
end
end
...
@@ -56,7 +57,7 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
...
@@ -56,7 +57,7 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
end
step
'I should only see the same deploy key once'
do
step
'I should only see the same deploy key once'
do
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
(
page
).
to
have_selector
(
'ul li'
,
count:
1
)
expect
(
page
).
to
have_selector
(
'ul li'
,
count:
1
)
end
end
end
end
...
@@ -66,8 +67,9 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
...
@@ -66,8 +67,9 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
end
step
'I click attach deploy key'
do
step
'I click attach deploy key'
do
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
click_link
'Enable'
click_button
'Enable'
expect
(
page
).
not_to
have_selector
(
'.fa-spinner'
)
end
end
end
end
...
...
spec/controllers/projects/deploy_keys_controller_spec.rb
0 → 100644
View file @
7a66cd68
require
'spec_helper'
describe
Projects
::
DeployKeysController
do
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:user
)
{
create
(
:user
)
}
before
do
project
.
team
<<
[
user
,
:master
]
sign_in
(
user
)
end
describe
'GET index'
do
let
(
:params
)
do
{
namespace_id:
project
.
namespace
,
project_id:
project
}
end
context
'when html requested'
do
it
'redirects to blob'
do
get
:index
,
params
expect
(
response
).
to
redirect_to
(
namespace_project_settings_repository_path
(
params
))
end
end
context
'when json requested'
do
let
(
:project2
)
{
create
(
:empty_project
,
:internal
)}
let
(
:project_private
)
{
create
(
:empty_project
,
:private
)}
let
(
:deploy_key_internal
)
do
create
(
:deploy_key
,
key:
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com'
)
end
let
(
:deploy_key_actual
)
do
create
(
:deploy_key
,
key:
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com'
)
end
let!
(
:deploy_key_public
)
{
create
(
:deploy_key
,
public:
true
)
}
let!
(
:deploy_keys_project_internal
)
do
create
(
:deploy_keys_project
,
project:
project2
,
deploy_key:
deploy_key_internal
)
end
let!
(
:deploy_keys_actual_project
)
do
create
(
:deploy_keys_project
,
project:
project
,
deploy_key:
deploy_key_actual
)
end
let!
(
:deploy_keys_project_private
)
do
create
(
:deploy_keys_project
,
project:
project_private
,
deploy_key:
create
(
:another_deploy_key
))
end
before
do
project2
.
team
<<
[
user
,
:developer
]
end
it
'returns json in a correct format'
do
get
:index
,
params
.
merge
(
format: :json
)
json
=
JSON
.
parse
(
response
.
body
)
expect
(
json
.
keys
).
to
match_array
(
%w(enabled_keys available_project_keys public_keys)
)
expect
(
json
[
'enabled_keys'
].
count
).
to
eq
(
1
)
expect
(
json
[
'available_project_keys'
].
count
).
to
eq
(
1
)
expect
(
json
[
'public_keys'
].
count
).
to
eq
(
1
)
end
end
end
end
spec/features/projects/deploy_keys_spec.rb
View file @
7a66cd68
require
'spec_helper'
require
'spec_helper'
describe
'Project deploy keys'
,
feature:
tru
e
do
describe
'Project deploy keys'
,
:js
,
:featur
e
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project_empty_repo
)
}
let
(
:project
)
{
create
(
:project_empty_repo
)
}
...
@@ -17,9 +17,13 @@ describe 'Project deploy keys', feature: true do
...
@@ -17,9 +17,13 @@ describe 'Project deploy keys', feature: true do
it
'removes association between project and deploy key'
do
it
'removes association between project and deploy key'
do
visit
namespace_project_settings_repository_path
(
project
.
namespace
,
project
)
visit
namespace_project_settings_repository_path
(
project
.
namespace
,
project
)
page
.
within
'.deploy-keys'
do
page
.
within
(
find
(
'.deploy-keys'
))
do
expect
{
click_on
'Remove'
}
expect
(
page
).
to
have_selector
(
'.deploy-keys li'
,
count:
1
)
.
to
change
{
project
.
deploy_keys
.
count
}.
by
(
-
1
)
click_on
'Remove'
expect
(
page
).
not_to
have_selector
(
'.fa-spinner'
,
count:
0
)
expect
(
page
).
to
have_selector
(
'.deploy-keys li'
,
count:
0
)
end
end
end
end
end
end
...
...
spec/javascripts/deploy_keys/components/action_btn_spec.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
eventHub
from
'
~/deploy_keys/eventhub
'
;
import
actionBtn
from
'
~/deploy_keys/components/action_btn.vue
'
;
describe
(
'
Deploy keys action btn
'
,
()
=>
{
const
data
=
getJSONFixture
(
'
deploy_keys/keys.json
'
);
const
deployKey
=
data
.
enabled_keys
[
0
];
let
vm
;
beforeEach
((
done
)
=>
{
const
ActionBtnComponent
=
Vue
.
extend
(
actionBtn
);
vm
=
new
ActionBtnComponent
({
propsData
:
{
deployKey
,
type
:
'
enable
'
,
},
}).
$mount
();
setTimeout
(
done
);
});
it
(
'
renders the type as uppercase
'
,
()
=>
{
expect
(
vm
.
$el
.
textContent
.
trim
(),
).
toBe
(
'
Enable
'
);
});
it
(
'
sends eventHub event with btn type
'
,
(
done
)
=>
{
spyOn
(
eventHub
,
'
$emit
'
);
vm
.
$el
.
click
();
setTimeout
(()
=>
{
expect
(
eventHub
.
$emit
,
).
toHaveBeenCalledWith
(
'
enable.key
'
,
deployKey
);
done
();
});
});
it
(
'
shows loading spinner after click
'
,
(
done
)
=>
{
vm
.
$el
.
click
();
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa
'
),
).
toBeDefined
();
done
();
});
});
it
(
'
disables button after click
'
,
(
done
)
=>
{
vm
.
$el
.
click
();
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
classList
.
contains
(
'
disabled
'
),
).
toBeTruthy
();
expect
(
vm
.
$el
.
getAttribute
(
'
disabled
'
),
).
toBe
(
'
disabled
'
);
done
();
});
});
});
spec/javascripts/deploy_keys/components/app_spec.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
eventHub
from
'
~/deploy_keys/eventhub
'
;
import
deployKeysApp
from
'
~/deploy_keys/components/app.vue
'
;
describe
(
'
Deploy keys app component
'
,
()
=>
{
const
data
=
getJSONFixture
(
'
deploy_keys/keys.json
'
);
let
vm
;
const
deployKeysResponse
=
(
request
,
next
)
=>
{
next
(
request
.
respondWith
(
JSON
.
stringify
(
data
),
{
status
:
200
,
}));
};
beforeEach
((
done
)
=>
{
const
Component
=
Vue
.
extend
(
deployKeysApp
);
Vue
.
http
.
interceptors
.
push
(
deployKeysResponse
);
vm
=
new
Component
({
propsData
:
{
endpoint
:
'
/test
'
,
},
}).
$mount
();
setTimeout
(
done
);
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptors
,
deployKeysResponse
);
});
it
(
'
renders loading icon
'
,
(
done
)
=>
{
vm
.
store
.
keys
=
{};
vm
.
isLoading
=
false
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.deploy-keys-panel
'
).
length
,
).
toBe
(
0
);
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
),
).
toBeDefined
();
done
();
});
});
it
(
'
renders keys panels
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.deploy-keys-panel
'
).
length
,
).
toBe
(
3
);
});
it
(
'
does not render key panels when keys object is empty
'
,
(
done
)
=>
{
vm
.
store
.
keys
=
{};
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.deploy-keys-panel
'
).
length
,
).
toBe
(
0
);
done
();
});
});
it
(
'
does not render public panel when empty
'
,
(
done
)
=>
{
vm
.
store
.
keys
.
public_keys
=
[];
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.deploy-keys-panel
'
).
length
,
).
toBe
(
2
);
done
();
});
});
it
(
'
re-fetches deploy keys when enabling a key
'
,
(
done
)
=>
{
const
key
=
data
.
public_keys
[
0
];
spyOn
(
vm
.
service
,
'
getKeys
'
);
spyOn
(
vm
.
service
,
'
enableKey
'
).
and
.
callFake
(()
=>
new
Promise
((
resolve
)
=>
{
resolve
();
setTimeout
(()
=>
{
expect
(
vm
.
service
.
getKeys
).
toHaveBeenCalled
();
done
();
});
}));
eventHub
.
$emit
(
'
enable.key
'
,
key
);
expect
(
vm
.
service
.
enableKey
).
toHaveBeenCalledWith
(
key
.
id
);
});
it
(
'
re-fetches deploy keys when disabling a key
'
,
(
done
)
=>
{
const
key
=
data
.
public_keys
[
0
];
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
true
);
spyOn
(
vm
.
service
,
'
getKeys
'
);
spyOn
(
vm
.
service
,
'
disableKey
'
).
and
.
callFake
(()
=>
new
Promise
((
resolve
)
=>
{
resolve
();
setTimeout
(()
=>
{
expect
(
vm
.
service
.
getKeys
).
toHaveBeenCalled
();
done
();
});
}));
eventHub
.
$emit
(
'
disable.key
'
,
key
);
expect
(
vm
.
service
.
disableKey
).
toHaveBeenCalledWith
(
key
.
id
);
});
it
(
'
calls disableKey when removing a key
'
,
(
done
)
=>
{
const
key
=
data
.
public_keys
[
0
];
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
true
);
spyOn
(
vm
.
service
,
'
getKeys
'
);
spyOn
(
vm
.
service
,
'
disableKey
'
).
and
.
callFake
(()
=>
new
Promise
((
resolve
)
=>
{
resolve
();
setTimeout
(()
=>
{
expect
(
vm
.
service
.
getKeys
).
toHaveBeenCalled
();
done
();
});
}));
eventHub
.
$emit
(
'
remove.key
'
,
key
);
expect
(
vm
.
service
.
disableKey
).
toHaveBeenCalledWith
(
key
.
id
);
});
it
(
'
hasKeys returns true when there are keys
'
,
()
=>
{
expect
(
vm
.
hasKeys
).
toEqual
(
3
);
});
});
spec/javascripts/deploy_keys/components/key_spec.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
DeployKeysStore
from
'
~/deploy_keys/store
'
;
import
key
from
'
~/deploy_keys/components/key.vue
'
;
describe
(
'
Deploy keys key
'
,
()
=>
{
let
vm
;
const
KeyComponent
=
Vue
.
extend
(
key
);
const
data
=
getJSONFixture
(
'
deploy_keys/keys.json
'
);
const
createComponent
=
(
deployKey
)
=>
{
const
store
=
new
DeployKeysStore
();
store
.
keys
=
data
;
vm
=
new
KeyComponent
({
propsData
:
{
deployKey
,
store
,
},
}).
$mount
();
};
describe
(
'
enabled key
'
,
()
=>
{
const
deployKey
=
data
.
enabled_keys
[
0
];
beforeEach
((
done
)
=>
{
createComponent
(
deployKey
);
setTimeout
(
done
);
});
it
(
'
renders the keys title
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.title
'
).
textContent
.
trim
(),
).
toContain
(
'
My title
'
);
});
it
(
'
renders human friendly formatted created date
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.key-created-at
'
).
textContent
.
trim
(),
).
toBe
(
`created
${
gl
.
utils
.
getTimeago
().
format
(
deployKey
.
created_at
)}
`
);
});
it
(
'
shows remove button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn
'
).
textContent
.
trim
(),
).
toBe
(
'
Remove
'
);
});
it
(
'
shows write access text when key has write access
'
,
(
done
)
=>
{
vm
.
deployKey
.
can_push
=
true
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.write-access-allowed
'
),
).
not
.
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.write-access-allowed
'
).
textContent
.
trim
(),
).
toBe
(
'
Write access allowed
'
);
done
();
});
});
});
describe
(
'
public keys
'
,
()
=>
{
const
deployKey
=
data
.
public_keys
[
0
];
beforeEach
((
done
)
=>
{
createComponent
(
deployKey
);
setTimeout
(
done
);
});
it
(
'
shows enable button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn
'
).
textContent
.
trim
(),
).
toBe
(
'
Enable
'
);
});
it
(
'
shows disable button when key is enabled
'
,
(
done
)
=>
{
vm
.
store
.
keys
.
enabled_keys
.
push
(
deployKey
);
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn
'
).
textContent
.
trim
(),
).
toBe
(
'
Disable
'
);
done
();
});
});
});
});
spec/javascripts/deploy_keys/components/keys_panel_spec.js
0 → 100644
View file @
7a66cd68
import
Vue
from
'
vue
'
;
import
DeployKeysStore
from
'
~/deploy_keys/store
'
;
import
deployKeysPanel
from
'
~/deploy_keys/components/keys_panel.vue
'
;
describe
(
'
Deploy keys panel
'
,
()
=>
{
const
data
=
getJSONFixture
(
'
deploy_keys/keys.json
'
);
let
vm
;
beforeEach
((
done
)
=>
{
const
DeployKeysPanelComponent
=
Vue
.
extend
(
deployKeysPanel
);
const
store
=
new
DeployKeysStore
();
store
.
keys
=
data
;
vm
=
new
DeployKeysPanelComponent
({
propsData
:
{
title
:
'
test
'
,
keys
:
data
.
enabled_keys
,
showHelpBox
:
true
,
store
,
},
}).
$mount
();
setTimeout
(
done
);
});
it
(
'
renders the title with keys count
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
h5
'
).
textContent
.
trim
(),
).
toContain
(
'
test
'
);
expect
(
vm
.
$el
.
querySelector
(
'
h5
'
).
textContent
.
trim
(),
).
toContain
(
`(
${
vm
.
keys
.
length
}
)`
);
});
it
(
'
renders list of keys
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
li
'
).
length
,
).
toBe
(
vm
.
keys
.
length
);
});
it
(
'
renders help box if keys are empty
'
,
(
done
)
=>
{
vm
.
keys
=
[];
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.settings-message
'
),
).
toBeDefined
();
expect
(
vm
.
$el
.
querySelector
(
'
.settings-message
'
).
textContent
.
trim
(),
).
toBe
(
'
No deploy keys found. Create one with the form above.
'
);
done
();
});
});
it
(
'
does not render help box if keys are empty & showHelpBox is false
'
,
(
done
)
=>
{
vm
.
keys
=
[];
vm
.
showHelpBox
=
false
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.settings-message
'
),
).
toBeNull
();
done
();
});
});
});
spec/javascripts/fixtures/deploy_keys.rb
0 → 100644
View file @
7a66cd68
require
'spec_helper'
describe
Projects
::
DeployKeysController
,
'(JavaScript fixtures)'
,
type: :controller
do
include
JavaScriptFixturesHelpers
let
(
:admin
)
{
create
(
:admin
)
}
let
(
:namespace
)
{
create
(
:namespace
,
name:
'frontend-fixtures'
)}
let
(
:project
)
{
create
(
:project_empty_repo
,
namespace:
namespace
,
path:
'todos-project'
)
}
let
(
:project2
)
{
create
(
:empty_project
,
:internal
)}
before
(
:all
)
do
clean_frontend_fixtures
(
'deploy_keys/'
)
end
before
(
:each
)
do
sign_in
(
admin
)
end
render_views
it
'deploy_keys/keys.json'
do
|
example
|
create
(
:deploy_key
,
public:
true
)
project_key
=
create
(
:deploy_key
,
key:
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com'
)
internal_key
=
create
(
:deploy_key
,
key:
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com'
)
create
(
:deploy_keys_project
,
project:
project
,
deploy_key:
project_key
)
create
(
:deploy_keys_project
,
project:
project2
,
deploy_key:
internal_key
)
get
:index
,
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
,
format: :json
expect
(
response
).
to
be_success
store_frontend_fixture
(
response
,
example
.
description
)
end
end
spec/serializers/deploy_key_entity_spec.rb
0 → 100644
View file @
7a66cd68
require
'spec_helper'
describe
DeployKeyEntity
do
include
RequestAwareEntity
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:empty_project
,
:internal
)}
let
(
:project_private
)
{
create
(
:empty_project
,
:private
)}
let
(
:deploy_key
)
{
create
(
:deploy_key
)
}
let!
(
:deploy_key_internal
)
{
create
(
:deploy_keys_project
,
project:
project
,
deploy_key:
deploy_key
)
}
let!
(
:deploy_key_private
)
{
create
(
:deploy_keys_project
,
project:
project_private
,
deploy_key:
deploy_key
)
}
let
(
:entity
)
{
described_class
.
new
(
deploy_key
,
user:
user
)
}
it
'returns deploy keys with projects a user can read'
do
expected_result
=
{
id:
deploy_key
.
id
,
user_id:
deploy_key
.
user_id
,
title:
deploy_key
.
title
,
fingerprint:
deploy_key
.
fingerprint
,
can_push:
deploy_key
.
can_push
,
destroyed_when_orphaned:
true
,
almost_orphaned:
false
,
created_at:
deploy_key
.
created_at
,
updated_at:
deploy_key
.
updated_at
,
projects:
[
{
id:
project
.
id
,
name:
project
.
name
,
full_path:
namespace_project_path
(
project
.
namespace
,
project
),
full_name:
project
.
full_name
}
]
}
expect
(
entity
.
as_json
).
to
eq
(
expected_result
)
end
end
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