Commit a72caacd authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch '342580-corpus-upload-commit-corpus' into 'master'

Implement corpus upload  - Commit Corpus

See merge request gitlab-org/gitlab!73893
parents b54b04ac 712188d9
......@@ -15,3 +15,4 @@ export const TYPE_USER = 'User';
export const TYPE_VULNERABILITY = 'Vulnerability';
export const TYPE_NOTE = 'Note';
export const TYPE_DISCUSSION = 'Discussion';
export const TYPE_PACKAGES_PACKAGE = 'Packages::Package';
......@@ -74,7 +74,11 @@ export default {
addCorpus() {
this.$apollo.mutate({
mutation: addCorpusMutation,
variables: { name: this.$options.i18n.newCorpus, projectPath: this.projectFullPath },
variables: {
name: this.$options.i18n.newCorpus,
projectPath: this.projectFullPath,
packageId: this.states.uploadState.uploadedPackageId,
},
});
},
resetCorpus() {
......
......@@ -30,7 +30,7 @@ export default {
corpusName: s__('CorpusManagement|Corpus name'),
uploadButtonText: __('Choose File...'),
uploadMessage: s__(
'CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 10GB',
'CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 5GB',
),
},
data() {
......@@ -170,7 +170,9 @@ export default {
<div v-if="isUploading" data-testid="upload-status" class="gl-mt-2">
<gl-loading-icon inline size="sm" />
{{ progressText }}
<gl-button size="small" @click="cancelUpload"> {{ __('Cancel') }} </gl-button>
<gl-button size="small" data-testid="cancel-upload" @click="cancelUpload">
{{ __('Cancel') }}
</gl-button>
</div>
</gl-form>
</template>
mutation addCorpus($projectPath: ID!, $name: String!) {
addCorpus(projectPath: $projectPath, name: $name) @client {
mutation addCorpus($projectPath: ID!, $name: String!, $packageId: Int!) {
addCorpus(projectPath: $projectPath, name: $name, packageId: $packageId) @client {
errors
}
}
mutation CorpusCreate($input: CorpusCreateInput!) {
corpusCreate(input: $input) {
errors
}
}
mutation uploadComplete($projectPath: ID!, $packageId: Int!) {
uploadComplete(projectPath: $projectPath, packageId: $packageId) @client {
errors
}
}
......@@ -7,5 +7,6 @@ query getCorpuses($projectPath: ID!) {
isUploading
progress
cancelSource
uploadedPackageId
}
}
......@@ -2,8 +2,12 @@ import produce from 'immer';
import { corpuses } from 'ee_jest/security_configuration/corpus_management/mock_data';
import { publishPackage } from '~/api/packages_api';
import axios from '~/lib/utils/axios_utils';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_PACKAGES_PACKAGE } from '~/graphql_shared/constants';
import getCorpusesQuery from '../queries/get_corpuses.query.graphql';
import updateProgress from '../mutations/update_progress.mutation.graphql';
import uploadComplete from '../mutations/upload_complete.mutation.graphql';
import corpusCreate from '../mutations/corpus_create.mutation.graphql';
export default {
Query: {
......@@ -22,12 +26,13 @@ export default {
isUploading: false,
progress: 0,
cancelSource: null,
uploadedPackageId: null,
__typename: 'UploadState',
};
},
},
Mutation: {
addCorpus: (_, { name, projectPath }, { cache }) => {
addCorpus: (_, { name, projectPath, packageId }, { cache, client }) => {
const sourceData = cache.readQuery({
query: getCorpusesQuery,
variables: { projectPath },
......@@ -54,6 +59,16 @@ export default {
});
cache.writeQuery({ query: getCorpusesQuery, data, variables: { projectPath } });
client.mutate({
mutation: corpusCreate,
variables: {
input: {
fullPath: projectPath,
packageId: convertToGraphQLId(TYPE_PACKAGES_PACKAGE, packageId),
},
},
});
},
deleteCorpus: (_, { name, projectPath }, { cache }) => {
const sourceData = cache.readQuery({
......@@ -91,19 +106,43 @@ export default {
variables: { projectPath },
});
const data = produce(sourceData, (draftState) => {
const targetData = produce(sourceData, (draftState) => {
const { uploadState } = draftState;
uploadState.isUploading = true;
uploadState.cancelSource = source;
});
cache.writeQuery({ query: getCorpusesQuery, data, variables: { projectPath } });
cache.writeQuery({ query: getCorpusesQuery, data: targetData, variables: { projectPath } });
publishPackage(
{ projectPath, name, version: 0, fileName: name, files },
{ status: 'hidden', select: 'package_file' },
{ onUploadProgress, cancelToken: source.token },
);
)
.then(({ data }) => {
client.mutate({
mutation: uploadComplete,
variables: { projectPath, packageId: data.package_id },
});
})
.catch((e) => {
/* TODO: Error handling */
});
},
uploadComplete: (_, { projectPath, packageId }, { cache }) => {
const sourceData = cache.readQuery({
query: getCorpusesQuery,
variables: { projectPath },
});
const data = produce(sourceData, (draftState) => {
const { uploadState } = draftState;
uploadState.isUploading = false;
uploadState.cancelSource = null;
uploadState.uploadedPackageId = packageId;
});
cache.writeQuery({ query: getCorpusesQuery, data, variables: { projectPath } });
},
updateProgress: (_, { projectPath, progress }, { cache }) => {
const sourceData = cache.readQuery({
......@@ -115,11 +154,6 @@ export default {
const { uploadState } = draftState;
uploadState.isUploading = true;
uploadState.progress = progress;
if (progress >= 100) {
uploadState.isUploading = false;
uploadState.cancelSource = null;
}
});
cache.writeQuery({ query: getCorpusesQuery, data, variables: { projectPath } });
......
......@@ -18,6 +18,7 @@ exports[`Corpus upload modal corpus modal uploading state does show the upload p
<button
class="btn btn-default btn-sm gl-button"
data-testid="cancel-upload"
type="button"
>
<!---->
......@@ -27,7 +28,9 @@ exports[`Corpus upload modal corpus modal uploading state does show the upload p
<span
class="gl-button-text"
>
Cancel
Cancel
</span>
</button>
</div>
......
import { createLocalVue, mount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { mount } from '@vue/test-utils';
import CorpusUploadForm from 'ee/security_configuration/corpus_management/components/corpus_upload_form.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
const TEST_PROJECT_FULL_PATH = '/namespace/project';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('Corpus upload modal', () => {
let wrapper;
......@@ -15,12 +10,12 @@ describe('Corpus upload modal', () => {
const findUploadAttachment = () => wrapper.find('[data-testid="upload-attachment-button"]');
const findUploadCorpus = () => wrapper.find('[data-testid="upload-corpus"]');
const findUploadStatus = () => wrapper.find('[data-testid="upload-status"]');
const findFileInput = () => wrapper.find({ ref: 'fileUpload' });
const findCancelButton = () => wrapper.find('[data-testid="cancel-upload"]');
const createComponent = (propsData, options = {}) => {
wrapper = mount(CorpusUploadForm, {
localVue,
propsData,
apolloProvider: createMockApollo(),
provide: {
projectFullPath: TEST_PROJECT_FULL_PATH,
},
......@@ -46,10 +41,6 @@ describe('Corpus upload modal', () => {
const props = {
states: {
mockedPackages: {
totalSize: 0,
data: [],
},
uploadState: {
isUploading: false,
progress: 0,
......@@ -75,6 +66,15 @@ describe('Corpus upload modal', () => {
it('does not show the upload progress', () => {
expect(findUploadStatus().exists()).toBe(false);
});
describe('selecting a file', () => {
it('transitions to selected state', async () => {
jest.spyOn(wrapper.vm, 'onFileUploadChange').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findFileInput().trigger('change');
expect(wrapper.vm.onFileUploadChange).toHaveBeenCalled();
});
});
});
describe('file selected state', () => {
......@@ -87,16 +87,11 @@ describe('Corpus upload modal', () => {
attachmentName,
corpusName,
files: [attachmentName],
uploadTimeout: null,
};
};
const props = {
states: {
mockedPackages: {
totalSize: 0,
data: [],
},
uploadState: {
isUploading: false,
progress: 0,
......@@ -122,6 +117,15 @@ describe('Corpus upload modal', () => {
it('does not show the upload progress', () => {
expect(findUploadStatus().exists()).toBe(false);
});
describe('clicking upload file', () => {
it('begins the file upload', async () => {
jest.spyOn(wrapper.vm, 'beginFileUpload').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findUploadCorpus().trigger('click');
expect(wrapper.vm.beginFileUpload).toHaveBeenCalled();
});
});
});
describe('uploading state', () => {
......@@ -134,16 +138,11 @@ describe('Corpus upload modal', () => {
attachmentName,
corpusName,
files: [attachmentName],
uploadTimeout: null,
};
};
const props = {
states: {
mockedPackages: {
totalSize: 0,
data: [],
},
uploadState: {
isUploading: true,
progress: 25,
......@@ -171,6 +170,13 @@ describe('Corpus upload modal', () => {
expect(findUploadStatus().exists()).toBe(true);
expect(findUploadStatus().element).toMatchSnapshot();
});
describe('clicking cancel button', () => {
it('emits the reset corpus event', () => {
findCancelButton().trigger('click');
expect(wrapper.emitted().resetCorpus).toBeTruthy();
});
});
});
describe('file uploaded state', () => {
......@@ -183,16 +189,11 @@ describe('Corpus upload modal', () => {
attachmentName,
corpusName,
files: [attachmentName],
uploadTimeout: null,
};
};
const props = {
states: {
mockedPackages: {
totalSize: 0,
data: [],
},
uploadState: {
isUploading: false,
progress: 100,
......
import { GlButton } from '@gitlab/ui';
import { GlButton, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import CorpusUpload from 'ee/security_configuration/corpus_management/components/corpus_upload.vue';
import CorpusUploadForm from 'ee/security_configuration/corpus_management/components/corpus_upload_form.vue';
const TEST_PROJECT_FULL_PATH = '/namespace/project';
describe('Corpus Upload', () => {
let wrapper;
const findModal = () => wrapper.findComponent(GlModal);
const findCorpusUploadForm = () => wrapper.findComponent(CorpusUploadForm);
const createComponentFactory = (mountFn = shallowMount) => (options = {}) => {
const defaultProps = { totalSize: 4e8 };
wrapper = mountFn(CorpusUpload, {
......@@ -37,5 +41,43 @@ describe('Corpus Upload', () => {
expect(wrapper.findComponent(GlButton).exists()).toBe(true);
expect(wrapper.element).toMatchSnapshot();
});
describe('addCorpus mutation', () => {
it('gets called when the add button is clicked from the modal', async () => {
createComponent();
jest.spyOn(wrapper.vm, 'addCorpus').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findModal().vm.$emit('primary');
expect(wrapper.vm.addCorpus).toHaveBeenCalled();
});
});
describe('resetCorpus mutation', () => {
it('gets called when the cancel button is clicked from the modal', async () => {
createComponent();
jest.spyOn(wrapper.vm, 'resetCorpus').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findModal().vm.$emit('canceled');
expect(wrapper.vm.resetCorpus).toHaveBeenCalled();
});
it('gets called when the upload form triggers a reset', async () => {
createComponent();
jest.spyOn(wrapper.vm, 'resetCorpus').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findCorpusUploadForm().vm.$emit('resetCorpus');
expect(wrapper.vm.resetCorpus).toHaveBeenCalled();
});
});
describe('uploadCorpus mutation', () => {
it('gets called when the upload file is clicked from the modal', async () => {
createComponent();
jest.spyOn(wrapper.vm, 'beginFileUpload').mockImplementation(() => {});
await wrapper.vm.$forceUpdate();
findCorpusUploadForm().vm.$emit('beginFileUpload');
expect(wrapper.vm.beginFileUpload).toHaveBeenCalled();
});
});
});
});
......@@ -9563,7 +9563,7 @@ msgstr ""
msgid "CorpusManagement|Latest Job:"
msgstr ""
msgid "CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 10GB"
msgid "CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 5GB"
msgstr ""
msgid "CorpusManagement|New upload"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment