diff --git a/src/breeding-insight/dao/SharedOntologyDAO.ts b/src/breeding-insight/dao/SharedOntologyDAO.ts new file mode 100644 index 000000000..c9ffa571d --- /dev/null +++ b/src/breeding-insight/dao/SharedOntologyDAO.ts @@ -0,0 +1,50 @@ +/* + * 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, Metadata, Response} from "@/breeding-insight/model/BiResponse"; +import * as api from "@/util/api"; +import {SharedProgramRequest} from "@/breeding-insight/model/SharedProgramRequest"; + +export class SharedOntologyDAO { + + static async get(programId: string): Promise { + const { data } = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/ontology/shared/programs`, + method: 'get' + }) as Response; + + return new BiResponse(data); + } + + static async share(programId: string, sharedProgramsRequest: SharedProgramRequest[]): Promise { + const { data } = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/ontology/shared/programs`, + method: 'post', + data: sharedProgramsRequest + }) as Response; + return new BiResponse(data); + } + + static async revoke(programId: string, sharedProgramId: string) { + const { data } = await api.call({ + url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/ontology/shared/programs/${sharedProgramId}`, + method: 'delete' + }) as Response; + + return new BiResponse(data); + } +} \ No newline at end of file diff --git a/src/breeding-insight/model/SharedProgram.ts b/src/breeding-insight/model/SharedProgram.ts new file mode 100644 index 000000000..55b2e9f9b --- /dev/null +++ b/src/breeding-insight/model/SharedProgram.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +export class SharedProgram { + programName: string; + programId: string; + shared: boolean; + accepted: boolean | undefined; + editable: boolean | undefined; + + constructor({programName, programId, shared, accepted, editable}: SharedProgram) { + this.programName = programName; + this.programId = programId; + this.shared = shared; + this.accepted = accepted; + this.editable = editable; + } +} \ No newline at end of file diff --git a/src/breeding-insight/model/SharedProgramRequest.ts b/src/breeding-insight/model/SharedProgramRequest.ts new file mode 100644 index 000000000..df7223ab2 --- /dev/null +++ b/src/breeding-insight/model/SharedProgramRequest.ts @@ -0,0 +1,27 @@ +/* + * 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. + */ + + +export class SharedProgramRequest { + programName: string; + programId: string; + + constructor({programName, programId}: SharedProgramRequest) { + this.programName = programName; + this.programId = programId; + } +} \ No newline at end of file diff --git a/src/breeding-insight/service/SharedOntologyService.ts b/src/breeding-insight/service/SharedOntologyService.ts new file mode 100644 index 000000000..a1578a8c2 --- /dev/null +++ b/src/breeding-insight/service/SharedOntologyService.ts @@ -0,0 +1,88 @@ +/* + * 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 {SharedProgram} from "@/breeding-insight/model/SharedProgram"; +import {BiResponse, Metadata} from "@/breeding-insight/model/BiResponse"; +import {TraitDAO} from "@/breeding-insight/dao/TraitDAO"; +import {ValidationErrorService} from "@/breeding-insight/service/ValidationErrorService"; +import {SharedOntologyDAO} from "@/breeding-insight/dao/SharedOntologyDAO"; +import {SharedProgramRequest} from "@/breeding-insight/model/SharedProgramRequest"; +import {ValidationError} from "@/breeding-insight/model/errors/ValidationError"; + +export class SharedOntologyService { + + static async get(programId: string): Promise<[SharedProgram[], Metadata]> { + if (!programId) throw 'Program ID required'; + + try { + const { result: { data }, metadata } = await SharedOntologyDAO.get(programId); + const sharedPrograms: SharedProgram[] = []; + for (const datum of data) { + sharedPrograms.push(new SharedProgram(datum)); + } + return [sharedPrograms, metadata]; + } catch (error) { + if (error.response && error.response.status === 404) { + throw error.response.message; + } else { + throw 'An unknown error has occurred'; + } + } + + } + + static async share(programId: string, shareRequests: SharedProgramRequest[]): Promise<[SharedProgram[], Metadata]> { + + try { + const { result: { data }, metadata } = await SharedOntologyDAO.share(programId, shareRequests); + // Parse into SharedPrograms + const sharedPrograms: SharedProgram[] = []; + for (const datum of data) { + sharedPrograms.push(new SharedProgram(datum)); + } + return [sharedPrograms, metadata]; + } catch (error) { + if (error.response && error.response.status === 404) { + throw error.response.message; + } else if (error.response && error.response.status == 422) { + const parsedErrors: ValidationError | string = ValidationErrorService.parseError(error); + // Stringify and format msg + throw ValidationErrorService.stringify(parsedErrors, {includeRowNum: false, includeField: false}).join(" "); + } else { + throw 'An unknown error has occurred'; + } + } + } + + static async revokeAll(programId: string, programsToRemove: string[]) { + + if (!programId) throw 'Program ID required'; + + // TODO: Collect errors + for (const programToRemove of programsToRemove) { + try { + await SharedOntologyDAO.revoke(programId, programToRemove); + } catch (error) { + if (error.response && error.response.status === 404) { + throw error.response.message; + } else { + throw 'An unknown error has occurred'; + } + } + } + } +} \ No newline at end of file diff --git a/src/breeding-insight/service/ValidationErrorService.ts b/src/breeding-insight/service/ValidationErrorService.ts index b63f82db3..8826e08a8 100644 --- a/src/breeding-insight/service/ValidationErrorService.ts +++ b/src/breeding-insight/service/ValidationErrorService.ts @@ -40,4 +40,27 @@ export class ValidationErrorService { } } } + + static stringify(errors: ValidationError | string, {includeRowNum, includeField}: {includeRowNum: boolean, includeField: boolean}) { + + const formattedErrors = []; + const isValidationError = errors instanceof ValidationError; + if (isValidationError){ + const validationErrors = errors as ValidationError; + if (validationErrors.rowErrors) { + for (const error of validationErrors.rowErrors){ + if (error.errors) { + for (const fieldError of error.errors){ + let msg = includeField ? `${fieldError.field}: ${fieldError.errorMessage}` : fieldError.errorMessage; + msg += includeRowNum ? ` in row ${error.rowIndex}` : ''; + formattedErrors.push(msg); + } + } + } + } + return formattedErrors; + } else { + return [errors]; + } + } } \ No newline at end of file diff --git a/src/components/modals/GenericModal.vue b/src/components/modals/GenericModal.vue new file mode 100644 index 000000000..20a7abf34 --- /dev/null +++ b/src/components/modals/GenericModal.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file diff --git a/src/components/program/SharedOntologyConfiguration.vue b/src/components/program/SharedOntologyConfiguration.vue new file mode 100644 index 000000000..cf3312fc2 --- /dev/null +++ b/src/components/program/SharedOntologyConfiguration.vue @@ -0,0 +1,247 @@ + + + + + \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 8e71272eb..e8076a244 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -66,6 +66,7 @@ import OntologyArchivedTable from "@/components/ontology/OntologyArchivedTable.v import PageNotFound from "@/views/PageNotFound.vue"; import Germplasm from "@/views/germplasm/Germplasm.vue"; import GermplasmLists from "@/views/germplasm/GermplasmLists.vue"; +import ProgramConfiguration from "@/views/program/ProgramConfiguration.vue"; Vue.use(VueRouter); @@ -228,6 +229,15 @@ const routes = [ layout: layouts.userSideBar }, component: ProgramUserManagement + }, + { + path: 'configure', + name: 'program-configuration', + meta: { + title: 'Program Configuration', + layout: layouts.userSideBar + }, + component: ProgramConfiguration } ] }, diff --git a/src/views/program/ProgramConfiguration.vue b/src/views/program/ProgramConfiguration.vue new file mode 100644 index 000000000..bb8f0658a --- /dev/null +++ b/src/views/program/ProgramConfiguration.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/src/views/program/ProgramManagement.vue b/src/views/program/ProgramManagement.vue index 426808f93..cb363250c 100644 --- a/src/views/program/ProgramManagement.vue +++ b/src/views/program/ProgramManagement.vue @@ -34,6 +34,11 @@ tag="li" active-class="is-active"> Users + + Configuration +