diff --git a/package.json b/package.json
index 64dcce36..717d6dfc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bi-web",
- "version": "v1.1.0",
+ "version": "v1.2.0+873",
"private": true,
"scripts": {
"build": "node $npm_package_config_task_path/build.js --dev-audit-level=critical --prod-audit-level=none",
@@ -96,5 +96,5 @@
"vue-cli-plugin-axios": "0.0.4",
"vue-template-compiler": "^2.7.14"
},
- "versionInfo": "https://github.com/Breeding-Insight/bi-web/releases/tag/v1.1.0"
+ "versionInfo": "https://github.com/Breeding-Insight/bi-web/commit/5c4d461021cff1e8ce8e69a4b2de0d43468358d7"
}
diff --git a/src/breeding-insight/dao/UserDAO.ts b/src/breeding-insight/dao/UserDAO.ts
index f93f69b5..9fa3425c 100644
--- a/src/breeding-insight/dao/UserDAO.ts
+++ b/src/breeding-insight/dao/UserDAO.ts
@@ -21,6 +21,7 @@ import {BiResponse} from "@/breeding-insight/model/BiResponse";
import {Role} from "@/breeding-insight/model/Role";
import {PaginationQuery} from "@/breeding-insight/model/PaginationQuery";
import {UserSort} from "@/breeding-insight/model/Sort";
+import {SearchRequest} from "@/breeding-insight/model/SearchRequest";
export class UserDAO {
@@ -101,6 +102,40 @@ export class UserDAO {
}
+ static getById(id: string): Promise {
+ return new Promise((resolve, reject) => {
+ api.call({ url: `${process.env.VUE_APP_BI_API_V1_PATH}/users/${id}`, method: 'get' })
+ .then((response: any) => {
+ const biResponse = new BiResponse(response.data);
+ resolve(biResponse);
+ }).catch((error) => reject(error));
+ });
+ }
+
+ static search(searchRequest: SearchRequest, {page, pageSize}: PaginationQuery, {field, order}: UserSort): Promise {
+
+ return new Promise(((resolve, reject) => {
+ const config = {
+ url: `${process.env.VUE_APP_BI_API_V1_PATH}/users/search`,
+ method: 'post',
+ data: searchRequest,
+ params: {
+ sortField: field,
+ sortOrder: order,
+ page,
+ pageSize
+ }
+ }
+ api.call(config)
+ .then((response: any) => {
+ const biResponse = new BiResponse(response.data);
+ resolve(biResponse);
+ }).catch((error) => {
+ reject(error);
+ })
+ }));
+ }
+
static updateSystemRoles(id: string, systemRoles: Array) {
return new Promise((resolve, reject) => {
diff --git a/src/breeding-insight/model/FilterRequest.ts b/src/breeding-insight/model/FilterRequest.ts
new file mode 100644
index 00000000..77a9edc4
--- /dev/null
+++ b/src/breeding-insight/model/FilterRequest.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 FilterRequest {
+ field: string;
+ value: string;
+
+ constructor(field: string, value: string) {
+ this.field = field;
+ this.value = value;
+ }
+}
diff --git a/src/breeding-insight/model/SearchRequest.ts b/src/breeding-insight/model/SearchRequest.ts
new file mode 100644
index 00000000..845327f3
--- /dev/null
+++ b/src/breeding-insight/model/SearchRequest.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 {FilterRequest} from "@/breeding-insight/model/FilterRequest";
+
+export class SearchRequest {
+ filters: FilterRequest[] = [];
+
+ constructor(filters: FilterRequest[]) {
+ this.filters = filters;
+ }
+}
diff --git a/src/breeding-insight/service/UserService.ts b/src/breeding-insight/service/UserService.ts
index b80eb6de..ce3433c0 100644
--- a/src/breeding-insight/service/UserService.ts
+++ b/src/breeding-insight/service/UserService.ts
@@ -24,13 +24,16 @@ import {Program} from "@/breeding-insight/model/Program";
import {ProgramUser} from "@/breeding-insight/model/ProgramUser";
import {PaginationQuery} from "@/breeding-insight/model/PaginationQuery";
import {ProgramSort, SortOrder, UserSort, UserSortField} from "@/breeding-insight/model/Sort";
+import {SearchRequest} from "@/breeding-insight/model/SearchRequest";
export class UserService {
static duplicateEmailMessage : string = 'Email is already in use.';
static errorCreateUser: string = 'Unable to create user';
static errorDeleteUser: string = 'Unable to archive user';
- static errorGetUsers: string = 'Error while trying to load roles';
+ static errorGetUsers: string = 'Error while trying to load users';
+ static errorSearchUsers: string = 'Error while searching users';
+ static errorGetUser: string = 'User not found';
static errorDeleteUserNotFound: string = 'Unable to find user to deactivate';
static errorDeleteUserNotAllowed: string = 'You are not allowed to deactivate this user.';
static errorPermissionsEditUser: string = "You don't have permissions to edit the roles of this user.";
@@ -153,6 +156,45 @@ export class UserService {
}));
}
+ static getById(id: string): Promise {
+ return new Promise((resolve, reject) => {
+ UserDAO.getById(id).then((biResponse: BiResponse) => {
+ const result: any = biResponse.result;
+ const role: Role | undefined = this.parseSystemRoles(result.systemRoles);
+ const programRoles: ProgramUser[] | undefined = this.parseProgramRoles(result.programRoles);
+ const user = new User(result.id, result.name, result.orcid, result.email, role, programRoles);
+ resolve(user);
+ }).catch((error) => {
+ error['errorMessage'] = this.errorGetUser;
+ reject(error);
+ });
+ });
+ }
+
+ static search(searchRequest: SearchRequest,
+ paginationQuery: PaginationQuery = new PaginationQuery(1, 50, true),
+ sort: UserSort = new UserSort(UserSortField.Name, SortOrder.Ascending)): Promise<[User[], Metadata]> {
+ return new Promise<[User[], Metadata]>(((resolve, reject) => {
+
+ UserDAO.search(searchRequest, paginationQuery, sort).then((biResponse) => {
+
+ // Parse our users into the vue users param
+ let users = biResponse.result.data.map((user: any) => {
+ const role: Role | undefined = this.parseSystemRoles(user.systemRoles);
+ const programRoles: ProgramUser[] | undefined = this.parseProgramRoles(user.programRoles);
+ return new User(user.id, user.name, user.orcid, user.email, role, programRoles);
+ });
+
+ resolve([users, biResponse.metadata]);
+
+ }).catch((error) => {
+ error['errorMessage'] = this.errorSearchUsers;
+ reject(error)
+ });
+
+ }));
+ }
+
static updateSystemRoles(user: User): Promise {
return new Promise((resolve, reject) => {
diff --git a/src/components/program/ProgramUsersTable.vue b/src/components/program/ProgramUsersTable.vue
index ef66b26c..05a9d512 100644
--- a/src/components/program/ProgramUsersTable.vue
+++ b/src/components/program/ProgramUsersTable.vue
@@ -191,6 +191,8 @@
import {
UPDATE_PROGRAM_USER_SORT
} from "@/store/sorting/mutation-types";
+ import {SearchRequest} from "@/breeding-insight/model/SearchRequest";
+ import {FilterRequest} from "@/breeding-insight/model/FilterRequest";
@Component({
components: { ExpandableTable, NewDataForm, BasicInputField, BasicSelectField, TableColumn,
@@ -218,7 +220,6 @@ export default class ProgramUsersTable extends Vue {
private activeProgram?: Program;
private activeUser?: User;
public users: ProgramUser[] = [];
- public systemUsers: User[] = [];
private deactivateActive: boolean = false;
private newUserActive: boolean = false;
@@ -252,7 +253,6 @@ export default class ProgramUsersTable extends Vue {
mounted() {
this.getRoles();
- this.getSystemUsers();
this.paginationChanged();
}
@@ -333,27 +333,28 @@ export default class ProgramUsersTable extends Vue {
}
- saveUser() {
+ async saveUser() {
this.newUser.program = this.activeProgram;
try {
- this.newUser = this.checkExistingUserByEmail(this.newUser, this.systemUsers);
+ this.newUser = await this.checkExistingUserByEmail(this.newUser);
} catch (err) {
this.$emit('show-error-notification', err);
this.newUserFormState.bus.$emit(DataFormEventBusHandler.SAVE_COMPLETE_EVENT);
return;
}
+ // if we found a system user by email in checkExistingUserByEmail then system user exists
+ const systemUserExisted = this.newUser.id !== undefined;
+
ProgramUserService.create(this.newUser).then((user: ProgramUser) => {
this.paginationController.updatePage(1);
this.paginationController.updateOnAdd();
this.getUsers();
- this.getSystemUsers();
// See if the user already existed
- //TODO: Reconsider when user search feature is added
- if (this.getSystemUserById(user, this.systemUsers)) {
+ if (systemUserExisted) {
this.$emit('show-success-notification', 'Success! Existing user ' + user.name + ' added to program.');
} else {
this.$emit('show-success-notification', 'Success! ' + this.newUser.name + ' added.');
@@ -361,13 +362,11 @@ export default class ProgramUsersTable extends Vue {
if(this.newUser.email === this.activeUser!.email) this.updateActiveUser();
- this.getSystemUsers();
this.newUser = new ProgramUser();
this.newUserActive = false;
}).catch((error) => {
this.$emit('show-error-notification', error.errorMessage);
this.getUsers();
- this.getSystemUsers();
}).finally(() => this.newUserFormState.bus.$emit(DataFormEventBusHandler.SAVE_COMPLETE_EVENT))
}
@@ -379,47 +378,42 @@ export default class ProgramUsersTable extends Vue {
Vue.prototype.$ability.update(rules);
}
- //TODO: Reconsider when user search feature is added
- getSystemUsers() {
+ //TODO: Do we still want this since orcid entry is removed?
+ async checkExistingUserByEmail(user: ProgramUser): Promise {
+ user.id = undefined;
+ // api call to check if user exists for email
+ // this was done on front-end before because we didn't have search endpoint at the time
+ const filter = new FilterRequest('email', user.email);
+ const request = new SearchRequest(filter);
- UserService.getAll().then(([users, metadata]) => {
- this.systemUsers = users;
- }).catch((error) => {
+ try {
+ const [users, metadata] = await UserService.search(request);
+ // email exists
+ if (users.length === 1) {
+ user.id = users[0].id;
+ }
+ } catch (error) {
// Display error that users cannot be loaded
this.$emit('show-error-notification', 'Error while trying to load system users');
throw error;
- });
-
- }
-
- //TODO: Reconsider when user search feature is added
- //TODO: Do we still want this since orcid entry is removed?
- checkExistingUserByEmail(user: ProgramUser, systemUsers: User[]): ProgramUser {
- user.id = undefined;
- let usersFound = 0;
- for (const systemUser of systemUsers){
- if (user.email === systemUser.email){
- usersFound += 1;
- if (systemUser.id){
- user.id = systemUser.id;
- }
- }
}
- if (usersFound > 1){
- throw "Email matches two different users.";
- }
return user;
}
- //TODO: Reconsider when user search feature is added
- getSystemUserById(user: ProgramUser, systemUsers: User[]): User | undefined {
- for (const systemUser of systemUsers){
- if (user.id === systemUser.id){
- return systemUser;
+ getSystemUserById(user: ProgramUser): User | undefined {
+
+ UserService.getById(user.id).then(([users, metadata]) => {
+ if (users.length === 1) {
+ return users[0];
}
- }
+ }).catch((error) => {
+ // Display error that users cannot be loaded
+ this.$emit('show-error-notification', 'Error while trying to load system user');
+ throw error;
+ });
+
return undefined;
}
diff --git a/src/views/experiments-and-observations/ExperimentDetails.vue b/src/views/experiments-and-observations/ExperimentDetails.vue
index 34e7273f..52d9cfc3 100644
--- a/src/views/experiments-and-observations/ExperimentDetails.vue
+++ b/src/views/experiments-and-observations/ExperimentDetails.vue
@@ -222,7 +222,7 @@ export default class ExperimentDetails extends ProgramsBase {
new ActionMenuItem('experiment-import-file', 'import-file', 'Import file', this.$ability.can('create', 'Import')),
new ActionMenuItem('experiment-download-file', 'download-file', 'Download file'),
new ActionMenuItem('experiment-add-collaborator', 'add-collaborator', 'Add Collaborator', this.$ability.can('manage', 'Collaborator')),
- // new ActionMenuItem('experiment-create-sub-entity-dataset', 'create-sub-entity-dataset', 'Create Sub-Entity Dataset'),
+ new ActionMenuItem('experiment-create-sub-entity-dataset', 'create-sub-entity-dataset', 'Create Sub-Entity Dataset'),
new ActionMenuItem('experiment-delete', 'delete-experiment', 'Delete experiment', this.$ability.can('delete', 'Experiment'))
];
diff --git a/src/views/import/Dataset.vue b/src/views/import/Dataset.vue
index 6ef10f36..78bd4fba 100644
--- a/src/views/import/Dataset.vue
+++ b/src/views/import/Dataset.vue
@@ -123,6 +123,7 @@
New Germplasm count: {{ statistics.Germplasm.newObjectCount }}
New Pedigree Connections: {{ statistics["Pedigree Connections"].newObjectCount }}
- Duplicate names detected and are highlighted in yellow and show a icon.
- Upon import duplicates will become independent database entries.
+ Duplicate names detected and are highlighted in yellow and show a icon.
diff --git a/task/serve.js b/task/serve.js
index 1579e94d..630fa327 100755
--- a/task/serve.js
+++ b/task/serve.js
@@ -25,10 +25,13 @@ const port = process.env.PORT || JSON.parse(fs.readFileSync('package.json', 'utf
(async () => {
let spinner = ora({prefixText: ' ', color: 'yellow'});
try {
+ // TODO: figure out the issue here
+ /*
spinner = spinner.start('sort package.json');
await execa('npx', ['sort-package-json@3.0.0'], {preferLocal: true});
spinner = spinner.clear()
.succeed('package.json sorted');
+ */
console.log(`App running at http://localhost:${port}`);
await execa.command(`vue-cli-service\ serve --port ${port}`);