summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/packages/details
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/packages/details')
-rw-r--r--app/assets/javascripts/packages/details/components/app.vue101
-rw-r--r--app/assets/javascripts/packages/details/components/file_sha.vue41
-rw-r--r--app/assets/javascripts/packages/details/components/installation_commands.vue4
-rw-r--r--app/assets/javascripts/packages/details/components/package_files.vue67
-rw-r--r--app/assets/javascripts/packages/details/store/actions.js33
-rw-r--r--app/assets/javascripts/packages/details/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/packages/details/store/mutations.js3
7 files changed, 214 insertions, 36 deletions
diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue
index bcfb17a1529..55ffe10a608 100644
--- a/app/assets/javascripts/packages/details/components/app.vue
+++ b/app/assets/javascripts/packages/details/components/app.vue
@@ -13,7 +13,7 @@ import {
import { mapActions, mapState } from 'vuex';
import { objectToQueryString } from '~/lib/utils/common_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import PackageListRow from '../../shared/components/package_list_row.vue';
import PackagesListLoader from '../../shared/components/packages_list_loader.vue';
@@ -24,7 +24,6 @@ import DependencyRow from './dependency_row.vue';
import InstallationCommands from './installation_commands.vue';
import PackageFiles from './package_files.vue';
import PackageHistory from './package_history.vue';
-import PackageTitle from './package_title.vue';
export default {
name: 'PackagesApp',
@@ -36,7 +35,9 @@ export default {
GlTab,
GlTabs,
GlSprintf,
- PackageTitle,
+ PackageTitle: () => import('./package_title.vue'),
+ TerraformTitle: () =>
+ import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'),
PackagesListLoader,
PackageListRow,
DependencyRow,
@@ -50,7 +51,18 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
+ inject: {
+ titleComponent: {
+ default: 'PackageTitle',
+ from: 'titleComponent',
+ },
+ },
trackingActions: { ...TrackingActions },
+ data() {
+ return {
+ fileToDelete: null,
+ };
+ },
computed: {
...mapState([
'projectName',
@@ -86,13 +98,10 @@ export default {
},
},
methods: {
- ...mapActions(['deletePackage', 'fetchPackageVersions']),
+ ...mapActions(['deletePackage', 'fetchPackageVersions', 'deletePackageFile']),
formatSize(size) {
return numberToHumanSize(size);
},
- cancelDelete() {
- this.$refs.deleteModal.hide();
- },
getPackageVersions() {
if (!this.packageEntity.versions) {
this.fetchPackageVersions();
@@ -108,12 +117,43 @@ export default {
const modalQuery = objectToQueryString({ [SHOW_DELETE_SUCCESS_ALERT]: true });
window.location.replace(`${returnTo}?${modalQuery}`);
},
+ handleFileDelete(file) {
+ this.track(TrackingActions.REQUEST_DELETE_PACKAGE_FILE);
+ this.fileToDelete = { ...file };
+ this.$refs.deleteFileModal.show();
+ },
+ confirmFileDelete() {
+ this.track(TrackingActions.DELETE_PACKAGE_FILE);
+ this.deletePackageFile(this.fileToDelete.id);
+ this.fileToDelete = null;
+ },
},
i18n: {
deleteModalTitle: s__(`PackageRegistry|Delete Package Version`),
deleteModalContent: s__(
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
+ deleteFileModalTitle: s__(`PackageRegistry|Delete Package File`),
+ deleteFileModalContent: s__(
+ `PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`,
+ ),
+ },
+ modal: {
+ packageDeletePrimaryAction: {
+ text: __('Delete'),
+ attributes: [
+ { variant: 'danger' },
+ { category: 'primary' },
+ { 'data-qa-selector': 'delete_modal_button' },
+ ],
+ },
+ fileDeletePrimaryAction: {
+ text: __('Delete'),
+ attributes: [{ variant: 'danger' }, { category: 'primary' }],
+ },
+ cancelAction: {
+ text: __('Cancel'),
+ },
},
};
</script>
@@ -127,7 +167,7 @@ export default {
/>
<div v-else class="packages-app">
- <package-title>
+ <component :is="titleComponent">
<template #delete-button>
<gl-button
v-if="canDelete"
@@ -140,7 +180,7 @@ export default {
{{ __('Delete') }}
</gl-button>
</template>
- </package-title>
+ </component>
<gl-tabs>
<gl-tab :title="__('Detail')">
@@ -159,7 +199,9 @@ export default {
<package-files
v-if="showFiles"
:package-files="packageFiles"
+ :can-delete="canDelete"
@download-file="track($options.trackingActions.PULL_PACKAGE)"
+ @delete-file="handleFileDelete"
/>
</gl-tab>
@@ -210,7 +252,15 @@ export default {
</gl-tab>
</gl-tabs>
- <gl-modal ref="deleteModal" class="js-delete-modal" modal-id="delete-modal">
+ <gl-modal
+ ref="deleteModal"
+ class="js-delete-modal"
+ modal-id="delete-modal"
+ :action-primary="$options.modal.packageDeletePrimaryAction"
+ :action-cancel="$options.modal.cancelAction"
+ @primary="confirmPackageDeletion"
+ @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE)"
+ >
<template #modal-title>{{ $options.i18n.deleteModalTitle }}</template>
<gl-sprintf :message="$options.i18n.deleteModalContent">
<template #version>
@@ -221,23 +271,22 @@ export default {
<strong>{{ packageEntity.name }}</strong>
</template>
</gl-sprintf>
+ </gl-modal>
- <template #modal-footer>
- <div class="gl-w-full">
- <div class="float-right">
- <gl-button @click="cancelDelete">{{ __('Cancel') }}</gl-button>
- <gl-button
- ref="modal-delete-button"
- variant="danger"
- category="primary"
- data-qa-selector="delete_modal_button"
- @click="confirmPackageDeletion"
- >
- {{ __('Delete') }}
- </gl-button>
- </div>
- </div>
- </template>
+ <gl-modal
+ ref="deleteFileModal"
+ modal-id="delete-file-modal"
+ :action-primary="$options.modal.fileDeletePrimaryAction"
+ :action-cancel="$options.modal.cancelAction"
+ @primary="confirmFileDelete"
+ @canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)"
+ >
+ <template #modal-title>{{ $options.i18n.deleteFileModalTitle }}</template>
+ <gl-sprintf v-if="fileToDelete" :message="$options.i18n.deleteFileModalContent">
+ <template #filename>
+ <strong>{{ fileToDelete.file_name }}</strong>
+ </template>
+ </gl-sprintf>
</gl-modal>
</div>
</template>
diff --git a/app/assets/javascripts/packages/details/components/file_sha.vue b/app/assets/javascripts/packages/details/components/file_sha.vue
new file mode 100644
index 00000000000..a25839be7e1
--- /dev/null
+++ b/app/assets/javascripts/packages/details/components/file_sha.vue
@@ -0,0 +1,41 @@
+<script>
+import { s__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+
+export default {
+ name: 'FileSha',
+ components: {
+ DetailsRow,
+ ClipboardButton,
+ },
+ props: {
+ sha: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ },
+ i18n: {
+ copyButtonTitle: s__('PackageRegistry|Copy SHA'),
+ },
+};
+</script>
+
+<template>
+ <details-row dashed>
+ <div class="gl-px-4">
+ {{ title }}:
+ {{ sha }}
+ <clipboard-button
+ :text="sha"
+ :title="$options.i18n.copyButtonTitle"
+ category="tertiary"
+ size="small"
+ />
+ </div>
+ </details-row>
+</template>
diff --git a/app/assets/javascripts/packages/details/components/installation_commands.vue b/app/assets/javascripts/packages/details/components/installation_commands.vue
index 28978913e6e..ed55d7fe782 100644
--- a/app/assets/javascripts/packages/details/components/installation_commands.vue
+++ b/app/assets/javascripts/packages/details/components/installation_commands.vue
@@ -1,5 +1,6 @@
<script>
-import { PackageType } from '../../shared/constants';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import { PackageType, TERRAFORM_PACKAGE_TYPE } from '../../shared/constants';
import ComposerInstallation from './composer_installation.vue';
import ConanInstallation from './conan_installation.vue';
import MavenInstallation from './maven_installation.vue';
@@ -16,6 +17,7 @@ export default {
[PackageType.NUGET]: NugetInstallation,
[PackageType.PYPI]: PypiInstallation,
[PackageType.COMPOSER]: ComposerInstallation,
+ [TERRAFORM_PACKAGE_TYPE]: TerraformInstallation,
},
props: {
packageEntity: {
diff --git a/app/assets/javascripts/packages/details/components/package_files.vue b/app/assets/javascripts/packages/details/components/package_files.vue
index 103d1f489bd..0563b612d04 100644
--- a/app/assets/javascripts/packages/details/components/package_files.vue
+++ b/app/assets/javascripts/packages/details/components/package_files.vue
@@ -1,8 +1,9 @@
<script>
-import { GlLink, GlTable } from '@gitlab/ui';
+import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
import { last } from 'lodash';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
+import FileSha from '~/packages/details/components/file_sha.vue';
import Tracking from '~/tracking';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -12,8 +13,13 @@ export default {
components: {
GlLink,
GlTable,
+ GlIcon,
+ GlDropdown,
+ GlDropdownItem,
+ GlButton,
FileIcon,
TimeAgoTooltip,
+ FileSha,
},
mixins: [Tracking.mixin()],
props: {
@@ -22,6 +28,11 @@ export default {
required: false,
default: () => [],
},
+ canDelete: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
computed: {
filesTableRows() {
@@ -39,7 +50,6 @@ export default {
{
key: 'name',
label: __('Name'),
- tdClass: 'gl-display-flex gl-align-items-center',
},
{
key: 'commit',
@@ -55,6 +65,13 @@ export default {
label: __('Created'),
class: 'gl-text-right',
},
+ {
+ key: 'actions',
+ label: '',
+ hide: !this.canDelete,
+ class: 'gl-text-right',
+ tdClass: 'gl-w-4',
+ },
].filter((c) => !c.hide);
},
},
@@ -62,6 +79,12 @@ export default {
formatSize(size) {
return numberToHumanSize(size);
},
+ hasDetails(item) {
+ return item.file_sha256 || item.file_md5 || item.file_sha1;
+ },
+ },
+ i18n: {
+ deleteFile: __('Delete file'),
},
};
</script>
@@ -74,10 +97,18 @@ export default {
:items="filesTableRows"
:tbody-tr-attr="{ 'data-testid': 'file-row' }"
>
- <template #cell(name)="{ item }">
+ <template #cell(name)="{ item, toggleDetails, detailsShowing }">
+ <gl-button
+ v-if="hasDetails(item)"
+ :icon="detailsShowing ? 'angle-up' : 'angle-down'"
+ :aria-label="detailsShowing ? __('Collapse') : __('Expand')"
+ category="tertiary"
+ size="small"
+ @click="toggleDetails"
+ />
<gl-link
:href="item.download_path"
- class="gl-relative gl-text-gray-500"
+ class="gl-text-gray-500"
data-testid="download-link"
@click="$emit('download-file')"
>
@@ -86,7 +117,7 @@ export default {
css-classes="gl-relative file-icon"
class="gl-mr-1 gl-relative"
/>
- <span class="gl-relative">{{ item.file_name }}</span>
+ <span>{{ item.file_name }}</span>
</gl-link>
</template>
@@ -103,6 +134,32 @@ export default {
<template #cell(created)="{ item }">
<time-ago-tooltip :time="item.created_at" />
</template>
+
+ <template #cell(actions)="{ item }">
+ <gl-dropdown category="tertiary" right>
+ <template #button-content>
+ <gl-icon name="ellipsis_v" />
+ </template>
+ <gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)">
+ {{ $options.i18n.deleteFile }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </template>
+
+ <template #row-details="{ item }">
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100"
+ >
+ <file-sha
+ v-if="item.file_sha256"
+ data-testid="sha-256"
+ title="SHA-256"
+ :sha="item.file_sha256"
+ />
+ <file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" />
+ <file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" />
+ </div>
+ </template>
</gl-table>
</div>
</template>
diff --git a/app/assets/javascripts/packages/details/store/actions.js b/app/assets/javascripts/packages/details/store/actions.js
index 87216366c8b..a03fa8d9d63 100644
--- a/app/assets/javascripts/packages/details/store/actions.js
+++ b/app/assets/javascripts/packages/details/store/actions.js
@@ -1,6 +1,10 @@
import Api from '~/api';
-import { deprecatedCreateFlash as createFlash } from '~/flash';
-import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/shared/constants';
+import createFlash from '~/flash';
+import {
+ DELETE_PACKAGE_ERROR_MESSAGE,
+ DELETE_PACKAGE_FILE_ERROR_MESSAGE,
+ DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
+} from '~/packages/shared/constants';
import { FETCH_PACKAGE_VERSIONS_ERROR } from '../constants';
import * as types from './mutation_types';
@@ -16,7 +20,7 @@ export const fetchPackageVersions = ({ commit, state }) => {
}
})
.catch(() => {
- createFlash(FETCH_PACKAGE_VERSIONS_ERROR);
+ createFlash({ message: FETCH_PACKAGE_VERSIONS_ERROR, type: 'warning' });
})
.finally(() => {
commit(types.SET_LOADING, false);
@@ -29,6 +33,27 @@ export const deletePackage = ({
},
}) => {
return Api.deleteProjectPackage(project_id, id).catch(() => {
- createFlash(DELETE_PACKAGE_ERROR_MESSAGE);
+ createFlash({ message: DELETE_PACKAGE_ERROR_MESSAGE, type: 'warning' });
});
};
+
+export const deletePackageFile = (
+ {
+ state: {
+ packageEntity: { project_id, id },
+ packageFiles,
+ },
+ commit,
+ },
+ fileId,
+) => {
+ return Api.deleteProjectPackageFile(project_id, id, fileId)
+ .then(() => {
+ const filtered = packageFiles.filter((f) => f.id !== fileId);
+ commit(types.UPDATE_PACKAGE_FILES, filtered);
+ createFlash({ message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE, type: 'success' });
+ })
+ .catch(() => {
+ createFlash({ message: DELETE_PACKAGE_FILE_ERROR_MESSAGE, type: 'warning' });
+ });
+};
diff --git a/app/assets/javascripts/packages/details/store/mutation_types.js b/app/assets/javascripts/packages/details/store/mutation_types.js
index 340d668819c..590f2d9f970 100644
--- a/app/assets/javascripts/packages/details/store/mutation_types.js
+++ b/app/assets/javascripts/packages/details/store/mutation_types.js
@@ -1,2 +1,3 @@
export const SET_LOADING = 'SET_LOADING';
export const SET_PACKAGE_VERSIONS = 'SET_PACKAGE_VERSIONS';
+export const UPDATE_PACKAGE_FILES = 'UPDATE_PACKAGE_FILES';
diff --git a/app/assets/javascripts/packages/details/store/mutations.js b/app/assets/javascripts/packages/details/store/mutations.js
index e113638311b..762fd5a4040 100644
--- a/app/assets/javascripts/packages/details/store/mutations.js
+++ b/app/assets/javascripts/packages/details/store/mutations.js
@@ -11,4 +11,7 @@ export default {
versions,
};
},
+ [types.UPDATE_PACKAGE_FILES](state, files) {
+ state.packageFiles = files;
+ },
};