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
fc241f9c
Commit
fc241f9c
authored
Nov 11, 2021
by
Nicolò Maria Mezzopera
Committed by
Olena Horal-Koretska
Nov 11, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Enable dependency proxy manifest lists
parent
2ac8faae
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
182 additions
and
27 deletions
+182
-27
app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
...ascripts/packages_and_registries/dependency_proxy/app.vue
+62
-5
app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue
...registries/dependency_proxy/components/manifests_list.vue
+8
-2
app/views/groups/dependency_proxies/show.html.haml
app/views/groups/dependency_proxies/show.html.haml
+2
-1
locale/gitlab.pot
locale/gitlab.pot
+4
-1
spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
...tend/packages_and_registries/dependency_proxy/app_spec.js
+86
-13
spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
...istries/dependency_proxy/components/manifest_list_spec.js
+18
-4
spec/frontend/packages_and_registries/dependency_proxy/mock_data.js
...end/packages_and_registries/dependency_proxy/mock_data.js
+2
-1
No files found.
app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
View file @
fc241f9c
<
script
>
import
{
GlAlert
,
GlFormGroup
,
GlFormInputGroup
,
GlSkeletonLoader
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlAlert
,
GlFormGroup
,
GlFormInputGroup
,
GlSkeletonLoader
,
GlSprintf
,
GlEmptyState
,
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
import
ManifestsList
from
'
~/packages_and_registries/dependency_proxy/components/manifests_list.vue
'
;
import
{
DEPENDENCY_PROXY_SETTINGS_DESCRIPTION
,
DEPENDENCY_PROXY_DOCS_PATH
,
...
...
@@ -13,15 +21,17 @@ import getDependencyProxyDetailsQuery from '~/packages_and_registries/dependency
export
default
{
components
:
{
GlFormGroup
,
GlAlert
,
GlEmptyState
,
GlFormGroup
,
GlFormInputGroup
,
GlSkeletonLoader
,
GlSprintf
,
ClipboardButton
,
TitleArea
,
GlSkeletonLoader
,
ManifestsList
,
},
inject
:
[
'
groupPath
'
,
'
dependencyProxyAvailable
'
],
inject
:
[
'
groupPath
'
,
'
dependencyProxyAvailable
'
,
'
noManifestsIllustration
'
],
i18n
:
{
proxyNotAvailableText
:
s__
(
'
DependencyProxy|Dependency Proxy feature is limited to public groups for now.
'
,
...
...
@@ -33,6 +43,7 @@ export default {
copyImagePrefixText
:
s__
(
'
DependencyProxy|Copy prefix
'
),
blobCountAndSize
:
s__
(
'
DependencyProxy|Contains %{count} blobs of images (%{size})
'
),
pageTitle
:
s__
(
'
DependencyProxy|Dependency Proxy
'
),
noManifestTitle
:
s__
(
'
DependencyProxy|There are no images in the cache
'
),
},
data
()
{
return
{
...
...
@@ -46,7 +57,7 @@ export default {
return
!
this
.
dependencyProxyAvailable
;
},
variables
()
{
return
{
fullPath
:
this
.
groupPath
,
first
:
GRAPHQL_PAGE_SIZE
}
;
return
this
.
queryVariables
;
},
},
},
...
...
@@ -62,6 +73,38 @@ export default {
dependencyProxyEnabled
()
{
return
this
.
group
?.
dependencyProxySetting
?.
enabled
;
},
queryVariables
()
{
return
{
fullPath
:
this
.
groupPath
,
first
:
GRAPHQL_PAGE_SIZE
};
},
pageInfo
()
{
return
this
.
group
.
dependencyProxyManifests
.
pageInfo
;
},
manifests
()
{
return
this
.
group
.
dependencyProxyManifests
.
nodes
;
},
},
methods
:
{
fetchNextPage
()
{
this
.
fetchMore
({
first
:
GRAPHQL_PAGE_SIZE
,
after
:
this
.
pageInfo
?.
endCursor
,
});
},
fetchPreviousPage
()
{
this
.
fetchMore
({
first
:
null
,
last
:
GRAPHQL_PAGE_SIZE
,
before
:
this
.
pageInfo
?.
startCursor
,
});
},
fetchMore
(
variables
)
{
this
.
$apollo
.
queries
.
group
.
fetchMore
({
variables
:
{
...
this
.
queryVariables
,
...
variables
},
updateQuery
(
_
,
{
fetchMoreResult
})
{
return
fetchMoreResult
;
},
});
},
},
};
</
script
>
...
...
@@ -103,6 +146,20 @@ export default {
</span>
</template>
</gl-form-group>
<manifests-list
v-if=
"manifests && manifests.length"
:manifests=
"manifests"
:pagination=
"pageInfo"
@
prev-page=
"fetchPreviousPage"
@
next-page=
"fetchNextPage"
/>
<gl-empty-state
v-else
:svg-path=
"noManifestsIllustration"
:title=
"$options.i18n.noManifestTitle"
/>
</div>
<gl-alert
v-else
:dismissible=
"false"
data-testid=
"proxy-disabled"
>
{{ $options.i18n.proxyDisabledText }}
...
...
app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue
View file @
fc241f9c
...
...
@@ -21,13 +21,18 @@ export default {
},
},
i18n
:
{
listTitle
:
s__
(
'
DependencyProxy|Manifest list
'
),
listTitle
:
s__
(
'
DependencyProxy|Image list
'
),
},
computed
:
{
showPagination
()
{
return
this
.
pagination
.
hasNextPage
||
this
.
pagination
.
hasPreviousPage
;
},
},
};
</
script
>
<
template
>
<div
class=
"gl-mt-
5
"
>
<div
class=
"gl-mt-
6
"
>
<h3
class=
"gl-font-base"
>
{{
$options
.
i18n
.
listTitle
}}
</h3>
<div
class=
"gl-border-t-1 gl-border-gray-100 gl-border-t-solid gl-display-flex gl-flex-direction-column"
...
...
@@ -36,6 +41,7 @@ export default {
</div>
<div
class=
"gl-display-flex gl-justify-content-center"
>
<gl-keyset-pagination
v-if=
"showPagination"
v-bind=
"pagination"
class=
"gl-mt-3"
@
prev=
"$emit('prev-page')"
...
...
app/views/groups/dependency_proxies/show.html.haml
View file @
fc241f9c
...
...
@@ -3,4 +3,5 @@
-
dependency_proxy_available
=
Feature
.
enabled?
(
:dependency_proxy_for_private_groups
,
default_enabled:
true
)
||
@group
.
public?
#js-dependency-proxy
{
data:
{
group_path:
@group
.
full_path
,
dependency_proxy_available:
dependency_proxy_available
.
to_s
}
}
dependency_proxy_available:
dependency_proxy_available
.
to_s
,
no_manifests_illustration:
image_path
(
'illustrations/docker-empty-state.svg'
)
}
}
locale/gitlab.pot
View file @
fc241f9c
...
...
@@ -11340,7 +11340,10 @@ msgstr ""
msgid "DependencyProxy|Enable Proxy"
msgstr ""
msgid "DependencyProxy|Manifest list"
msgid "DependencyProxy|Image list"
msgstr ""
msgid "DependencyProxy|There are no images in the cache"
msgstr ""
msgid "Depends on %d merge request being merged"
...
...
spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
View file @
fc241f9c
import
{
GlFormInputGroup
,
GlFormGroup
,
GlSkeletonLoader
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlFormInputGroup
,
GlFormGroup
,
GlSkeletonLoader
,
GlSprintf
,
GlEmptyState
,
}
from
'
@gitlab/ui
'
;
import
{
createLocalVue
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
stripTypenames
}
from
'
helpers/graphql_helpers
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
GRAPHQL_PAGE_SIZE
}
from
'
~/packages_and_registries/dependency_proxy/constants
'
;
import
DependencyProxyApp
from
'
~/packages_and_registries/dependency_proxy/app.vue
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
ManifestsList
from
'
~/packages_and_registries/dependency_proxy/components/manifests_list.vue
'
;
import
getDependencyProxyDetailsQuery
from
'
~/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql
'
;
import
{
proxyDetailsQuery
,
proxyData
}
from
'
./mock_data
'
;
import
{
proxyDetailsQuery
,
proxyData
,
pagination
,
proxyManifests
}
from
'
./mock_data
'
;
const
localVue
=
createLocalVue
();
describe
(
'
DependencyProxyApp
'
,
()
=>
{
let
wrapper
;
let
apolloProvider
;
let
resolver
;
const
provideDefaults
=
{
groupPath
:
'
gitlab-org
'
,
dependencyProxyAvailable
:
true
,
noManifestsIllustration
:
'
noManifestsIllustration
'
,
};
function
createComponent
({
provide
=
provideDefaults
,
resolver
=
jest
.
fn
().
mockResolvedValue
(
proxyDetailsQuery
()),
}
=
{})
{
function
createComponent
({
provide
=
provideDefaults
}
=
{})
{
localVue
.
use
(
VueApollo
);
const
requestHandlers
=
[[
getDependencyProxyDetailsQuery
,
resolver
]];
...
...
@@ -53,6 +61,12 @@ describe('DependencyProxyApp', () => {
const
findSkeletonLoader
=
()
=>
wrapper
.
findComponent
(
GlSkeletonLoader
);
const
findMainArea
=
()
=>
wrapper
.
findByTestId
(
'
main-area
'
);
const
findProxyCountText
=
()
=>
wrapper
.
findByTestId
(
'
proxy-count
'
);
const
findManifestList
=
()
=>
wrapper
.
findComponent
(
ManifestsList
);
const
findEmptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
beforeEach
(()
=>
{
resolver
=
jest
.
fn
().
mockResolvedValue
(
proxyDetailsQuery
());
});
afterEach
(()
=>
{
wrapper
.
destroy
();
...
...
@@ -78,8 +92,8 @@ describe('DependencyProxyApp', () => {
});
it
(
'
does not call the graphql endpoint
'
,
async
()
=>
{
const
resolver
=
jest
.
fn
().
mockResolvedValue
(
proxyDetailsQuery
());
createComponent
({
...
createComponentArguments
,
resolver
});
resolver
=
jest
.
fn
().
mockResolvedValue
(
proxyDetailsQuery
());
createComponent
({
...
createComponentArguments
});
await
waitForPromises
();
...
...
@@ -145,14 +159,73 @@ describe('DependencyProxyApp', () => {
it
(
'
from group has a description with proxy count
'
,
()
=>
{
expect
(
findProxyCountText
().
text
()).
toBe
(
'
Contains 2 blobs of images (1024 Bytes)
'
);
});
describe
(
'
manifest lists
'
,
()
=>
{
describe
(
'
when there are no manifests
'
,
()
=>
{
beforeEach
(()
=>
{
resolver
=
jest
.
fn
().
mockResolvedValue
(
proxyDetailsQuery
({
extend
:
{
dependencyProxyManifests
:
{
nodes
:
[],
pageInfo
:
pagination
()
}
},
}),
);
createComponent
();
return
waitForPromises
();
});
it
(
'
shows the empty state message
'
,
()
=>
{
expect
(
findEmptyState
().
props
()).
toMatchObject
({
svgPath
:
provideDefaults
.
noManifestsIllustration
,
title
:
DependencyProxyApp
.
i18n
.
noManifestTitle
,
});
});
it
(
'
hides the list
'
,
()
=>
{
expect
(
findManifestList
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when there are manifests
'
,
()
=>
{
it
(
'
hides the empty state message
'
,
()
=>
{
expect
(
findEmptyState
().
exists
()).
toBe
(
false
);
});
it
(
'
shows list
'
,
()
=>
{
expect
(
findManifestList
().
props
()).
toMatchObject
({
manifests
:
proxyManifests
(),
pagination
:
stripTypenames
(
pagination
()),
});
});
it
(
'
prev-page event on list fetches the previous page
'
,
()
=>
{
findManifestList
().
vm
.
$emit
(
'
prev-page
'
);
expect
(
resolver
).
toHaveBeenCalledWith
({
before
:
pagination
().
startCursor
,
first
:
null
,
fullPath
:
provideDefaults
.
groupPath
,
last
:
GRAPHQL_PAGE_SIZE
,
});
});
it
(
'
next-page event on list fetches the next page
'
,
()
=>
{
findManifestList
().
vm
.
$emit
(
'
next-page
'
);
expect
(
resolver
).
toHaveBeenCalledWith
({
after
:
pagination
().
endCursor
,
first
:
GRAPHQL_PAGE_SIZE
,
fullPath
:
provideDefaults
.
groupPath
,
});
});
});
});
});
describe
(
'
when the dependency proxy is disabled
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
resolver
:
jest
.
fn
()
.
mockResolvedValue
(
proxyDetailsQuery
({
extendSettings
:
{
enabled
:
false
}
})),
});
resolver
=
jest
.
fn
()
.
mockResolvedValue
(
proxyDetailsQuery
({
extendSettings
:
{
enabled
:
false
}
}));
createComponent
();
return
waitForPromises
();
});
...
...
spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
View file @
fc241f9c
...
...
@@ -26,42 +26,56 @@ describe('Manifests List', () => {
const
findRows
=
()
=>
wrapper
.
findAllComponents
(
ManifestRow
);
const
findPagination
=
()
=>
wrapper
.
findComponent
(
GlKeysetPagination
);
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
has the correct title
'
,
()
=>
{
createComponent
();
expect
(
wrapper
.
text
()).
toContain
(
Component
.
i18n
.
listTitle
);
});
it
(
'
shows a row for every manifest
'
,
()
=>
{
createComponent
();
expect
(
findRows
().
length
).
toBe
(
defaultProps
.
manifests
.
length
);
});
it
(
'
binds a manifest to each row
'
,
()
=>
{
createComponent
();
expect
(
findRows
().
at
(
0
).
props
()).
toMatchObject
({
manifest
:
defaultProps
.
manifests
[
0
],
});
});
describe
(
'
pagination
'
,
()
=>
{
it
(
'
is hidden when there is no next or prev pages
'
,
()
=>
{
createComponent
({
...
defaultProps
,
pagination
:
{}
});
expect
(
findPagination
().
exists
()).
toBe
(
false
);
});
it
(
'
has the correct props
'
,
()
=>
{
createComponent
();
expect
(
findPagination
().
props
()).
toMatchObject
({
...
defaultProps
.
pagination
,
});
});
it
(
'
emits the next-page event
'
,
()
=>
{
createComponent
();
findPagination
().
vm
.
$emit
(
'
next
'
);
expect
(
wrapper
.
emitted
(
'
next-page
'
)).
toEqual
([[]]);
});
it
(
'
emits the prev-page event
'
,
()
=>
{
createComponent
();
findPagination
().
vm
.
$emit
(
'
prev
'
);
expect
(
wrapper
.
emitted
(
'
prev-page
'
)).
toEqual
([[]]);
...
...
spec/frontend/packages_and_registries/dependency_proxy/mock_data.js
View file @
fc241f9c
...
...
@@ -21,7 +21,7 @@ export const pagination = (extend) => ({
...
extend
,
});
export
const
proxyDetailsQuery
=
({
extendSettings
=
{}
}
=
{})
=>
({
export
const
proxyDetailsQuery
=
({
extendSettings
=
{}
,
extend
}
=
{})
=>
({
data
:
{
group
:
{
...
proxyData
(),
...
...
@@ -34,6 +34,7 @@ export const proxyDetailsQuery = ({ extendSettings = {} } = {}) => ({
nodes
:
proxyManifests
(),
pageInfo
:
pagination
(),
},
...
extend
,
},
},
});
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