diff --git a/package-lock.json b/package-lock.json index e8cbddb6e..494aa6ebf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bi-web", - "version": "v1.0.0+783", + "version": "v1.0.0+789", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bi-web", - "version": "v1.0.0+783", + "version": "v1.0.0+789", "hasInstallScript": true, "dependencies": { "@casl/ability": "~4.0.0", @@ -8581,13 +8581,20 @@ } }, "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -10960,9 +10967,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -10972,6 +10979,11 @@ ], "engines": { "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, "node_modules/for-in": { @@ -31923,12 +31935,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "decamelize": { @@ -33867,9 +33879,9 @@ "requires": {} }, "follow-redirects": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true }, "for-in": { diff --git a/src/assets/scss/main.scss b/src/assets/scss/main.scss index fedd7dd2d..66397d521 100644 --- a/src/assets/scss/main.scss +++ b/src/assets/scss/main.scss @@ -945,7 +945,7 @@ tr:nth-child(odd) td.db-filled { margin-right: 10px; } -.sub-entity-dataset-modal, .experiment-observations-download-button, .experiment-observation-add-collaborator-button, .experiment-observation-remove-collaborator-button { +.sub-entity-dataset-modal, .experiment-observations-download-button, .experiment-observation-add-collaborator-button, .experiment-observation-remove-collaborator-button, .germplasm-list-deletion-button { .modal { .modal-card { width: $medium-modal-content-width; diff --git a/src/breeding-insight/dao/GermplasmDAO.ts b/src/breeding-insight/dao/GermplasmDAO.ts index af5eeb1f2..33072750b 100644 --- a/src/breeding-insight/dao/GermplasmDAO.ts +++ b/src/breeding-insight/dao/GermplasmDAO.ts @@ -30,7 +30,7 @@ export class GermplasmDAO { config.url = `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/brapi/v2/lists`; config.method = 'get'; config.programId = programId; - config.params = {listType: ListType.Germplasm}; + config.params = {listType: ListType.GERMPLASM}; if (paginationQuery.page) config.params.page = paginationQuery.page - 1; if (paginationQuery.pageSize) config.params.pageSize = paginationQuery.pageSize; diff --git a/src/breeding-insight/model/GermplasmFilter.ts b/src/breeding-insight/model/ListFilter.ts similarity index 86% rename from src/breeding-insight/model/GermplasmFilter.ts rename to src/breeding-insight/model/ListFilter.ts index 0a0644472..e5bd51ffb 100644 --- a/src/breeding-insight/model/GermplasmFilter.ts +++ b/src/breeding-insight/model/ListFilter.ts @@ -18,14 +18,17 @@ import {GermplasmSortField} from "@/breeding-insight/model/Sort"; import {BaseFilter} from "@/breeding-insight/model/BaseFilter"; -export enum GermplasmBIField { +export enum ListBIField { ListDbId = "listDbId", ListName = "listName", } -export class GermplasmFilter extends BaseFilter { - [GermplasmBIField.ListDbId]: string; - [GermplasmBIField.ListName]: string; +export class ListFilter extends BaseFilter { + [ListBIField.ListDbId]: string; + [ListBIField.ListName]: string; +} + +export class GermplasmFilter extends ListFilter { [GermplasmSortField.AccessionNumber]: string; [GermplasmSortField.DefaultDisplayName]: string; [GermplasmSortField.BreedingMethod]: string; diff --git a/src/breeding-insight/service/GermplasmService.ts b/src/breeding-insight/service/GermplasmService.ts index 7049e37f2..0c6903b4f 100644 --- a/src/breeding-insight/service/GermplasmService.ts +++ b/src/breeding-insight/service/GermplasmService.ts @@ -24,7 +24,8 @@ import {Germplasm} from "@/breeding-insight/brapi/model/germplasm"; import {Result, ResultGenerator} from "@/breeding-insight/model/Result"; import {SortOrder} from "@/breeding-insight/model/Sort"; import * as api from "@/util/api"; -import {GermplasmFilter} from "@/breeding-insight/model/GermplasmFilter"; +import {GermplasmFilter} from "@/breeding-insight/model/ListFilter"; + export class GermplasmService { @@ -34,7 +35,7 @@ export class GermplasmService { { listDbId, listName, ...brapiFilters }: GermplasmFilter): Promise { //Form the query params including sorting, pagination, and filtering - let params: any = { ...brapiFilters }; + let params: any = {listDbId: listDbId, ...brapiFilters }; if (sort.field) { params['sortField'] = sort.field; @@ -67,8 +68,8 @@ export class GermplasmService { } //Get the list germplasm - const {data} = await api.call({ - url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/germplasm/lists/${listId}/records`, + const {data}: any = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/brapi/v2/germplasm`, method: 'get', params: params }) as Response; diff --git a/src/breeding-insight/service/ListService.ts b/src/breeding-insight/service/ListService.ts new file mode 100644 index 000000000..8b5b97d29 --- /dev/null +++ b/src/breeding-insight/service/ListService.ts @@ -0,0 +1,93 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {BiResponse} from "@/breeding-insight/model/BiResponse"; +import {SortOrder} from "@/breeding-insight/model/Sort"; +import * as api from "@/util/api"; +import {ListType} from "@/util/ListType"; + +export class ListService { + static async deleteList(programId: string | undefined, listDbId: string) { + if (programId == undefined) { + throw new Error("Not valid program"); + } + try { + const response = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/brapi/v2/lists/${listDbId}`, + method: 'delete', + params: { hardDelete: true } + }) as Response; + + // If we get here, it means the call was successful (likely 200 OK) + return new BiResponse({ success: true }); + + } catch (error) { + // Check if the error is actually a 204 No Content response since http clients can be configured to + // automatically throw an error if the response has no content + if (error.response && error.response.status === 204) { + // This is actually a successful deletion + return new BiResponse({ success: true }); + } + + // For other errors, log and rethrow + console.error('Error in deleteList:', error); + throw error; + } + } + + static async getLists(listType: ListType, + programId: string, + sort: { field: T, order: SortOrder }, + pagination: {pageSize: number, page: number}, + filters?: any): Promise { + if (!programId) throw 'Program ID required'; + + // Set list type, sort, and pagination + let params: any = { listType }; + + if(filters) { + params = { listType, ...filters }; + } + + if (sort.field) { + params['sortField'] = sort.field; + } + if (sort.order) { + params['sortOrder'] = sort.order; + } + if (pagination.page || pagination.page == 0) { //have to account for 0-index pagination since 0 falsy + params['page'] = pagination.page; + } + if (pagination.pageSize) { + params['pageSize'] = pagination.pageSize; + } + + // Make the GET call + try { + const { data }: any = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/brapi/v2/lists`, + method: 'get', + params: params + }); + + return new BiResponse(data); + + } catch (error) { + throw error; + } + } +} diff --git a/src/components/germplasm/GermplasmListDeletionlModal.vue b/src/components/germplasm/GermplasmListDeletionlModal.vue new file mode 100644 index 000000000..b24ec3ee5 --- /dev/null +++ b/src/components/germplasm/GermplasmListDeletionlModal.vue @@ -0,0 +1,89 @@ + + + + + \ No newline at end of file diff --git a/src/components/germplasm/GermplasmTable.vue b/src/components/germplasm/GermplasmTable.vue index 974efb163..2691c40ba 100644 --- a/src/components/germplasm/GermplasmTable.vue +++ b/src/components/germplasm/GermplasmTable.vue @@ -94,7 +94,7 @@ import { } from "@/breeding-insight/model/Sort"; import {UPDATE_GERMPLASM_SORT} from "@/store/sorting/mutation-types"; import { PaginationQuery } from '@/breeding-insight/model/PaginationQuery'; -import {GermplasmFilter} from "@/breeding-insight/model/GermplasmFilter"; +import {GermplasmFilter} from "@/breeding-insight/model/ListFilter"; @Component({ mixins: [validationMixin], diff --git a/src/config/AppAbility.ts b/src/config/AppAbility.ts index f3cb76e4c..590f043af 100644 --- a/src/config/AppAbility.ts +++ b/src/config/AppAbility.ts @@ -19,7 +19,8 @@ import {Ability, AbilityClass} from '@casl/ability'; type Actions = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'archive' | 'access' | 'submit'; type Subjects = 'ProgramUser' | 'Location' | 'User' | 'AdminSection' | 'Trait' | 'Import' | 'ProgramConfiguration' | 'Submission' - | 'Experiment' | 'Germplasm' | 'Ontology' | 'SampleManagement' | 'ProgramAdministration' | 'JobManagement' | 'Collaborator' | 'BrAPI' ; + | 'Experiment' | 'Germplasm' | 'Ontology' | 'SampleManagement' | 'ProgramAdministration' | 'JobManagement' | 'Collaborator' | 'BrAPI' + | 'List'; export type AppAbility = Ability<[Actions, Subjects]>; export const AppAbility = Ability as AbilityClass; \ No newline at end of file diff --git a/src/config/ability.ts b/src/config/ability.ts index b91c01900..afb865d69 100644 --- a/src/config/ability.ts +++ b/src/config/ability.ts @@ -59,6 +59,7 @@ const rolePermissions: Record = { can('access', 'BrAPI'); can('access', 'JobManagement'); can('manage', 'Collaborator'); + can('delete', 'List'); }, systemadministrator(user, { can }) { @@ -79,6 +80,7 @@ const rolePermissions: Record = { can('access', 'BrAPI'); can('access', 'JobManagement'); can('manage', 'Collaborator'); + can('delete', 'List'); } }; diff --git a/src/store/filtering/getters.ts b/src/store/filtering/getters.ts index b8d8a81a9..cfb45523a 100644 --- a/src/store/filtering/getters.ts +++ b/src/store/filtering/getters.ts @@ -18,7 +18,7 @@ import {GetterTree} from 'vuex'; import {RootState} from "@/store/types"; import {FilterState} from "@/store/filtering/types"; -import {GermplasmFilter} from "@/breeding-insight/model/GermplasmFilter"; +import {GermplasmFilter} from "@/breeding-insight/model/ListFilter"; export const getters: GetterTree = { // germplasm diff --git a/src/store/filtering/index.ts b/src/store/filtering/index.ts index 12cb5078b..c0b5b6dff 100644 --- a/src/store/filtering/index.ts +++ b/src/store/filtering/index.ts @@ -20,7 +20,7 @@ import {getters} from '@/store/filtering/getters'; import {mutations} from '@/store/filtering/mutations'; import {RootState} from '@/store/types'; import {FilterState} from "@/store/filtering/types"; -import {GermplasmFilter} from "@/breeding-insight/model/GermplasmFilter"; +import {GermplasmFilter} from "@/breeding-insight/model/ListFilter"; export let state: FilterState; state = { diff --git a/src/store/filtering/mutations.ts b/src/store/filtering/mutations.ts index 8a6212a69..5976d9bc1 100644 --- a/src/store/filtering/mutations.ts +++ b/src/store/filtering/mutations.ts @@ -17,7 +17,7 @@ import {MutationTree} from 'vuex'; import {FilterState} from "@/store/filtering/types"; -import {GermplasmFilter} from "@/breeding-insight/model/GermplasmFilter"; +import {GermplasmFilter} from "@/breeding-insight/model/ListFilter"; import {UPDATE_GERMPLASM_FILTER} from "@/store/filtering/mutation-types"; export const mutations: MutationTree = { diff --git a/src/store/filtering/types.ts b/src/store/filtering/types.ts index 3d1c7b83f..6d9dd0daa 100644 --- a/src/store/filtering/types.ts +++ b/src/store/filtering/types.ts @@ -1,4 +1,4 @@ -import { GermplasmFilter } from "@/breeding-insight/model/GermplasmFilter"; +import { GermplasmFilter } from "@/breeding-insight/model/ListFilter"; export interface FilterState { // germplasm table diff --git a/src/util/ListType.ts b/src/util/ListType.ts index 09a2b11c6..23df7121f 100644 --- a/src/util/ListType.ts +++ b/src/util/ListType.ts @@ -16,5 +16,5 @@ */ export enum ListType { - Germplasm = "germplasm", + GERMPLASM = "germplasm", } diff --git a/src/views/germplasm/GermplasmByList.vue b/src/views/germplasm/GermplasmByList.vue index 821a852f0..8db93ead0 100644 --- a/src/views/germplasm/GermplasmByList.vue +++ b/src/views/germplasm/GermplasmByList.vue @@ -18,45 +18,100 @@