| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| package com.epam.datalab.backendapi.dao; |
| |
| import com.epam.datalab.backendapi.util.DateRemoverUtil; |
| import com.epam.datalab.dto.exploratory.LibInstallDTO; |
| import com.epam.datalab.dto.exploratory.LibInstallStatusDTO; |
| import com.epam.datalab.dto.exploratory.LibStatus; |
| import com.epam.datalab.exceptions.DatalabException; |
| import com.epam.datalab.model.ResourceType; |
| import com.epam.datalab.model.library.Library; |
| import com.mongodb.client.model.Projections; |
| import org.apache.commons.lang3.StringUtils; |
| import org.bson.Document; |
| import org.bson.conversions.Bson; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.function.Predicate; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import static com.epam.datalab.backendapi.dao.ExploratoryDAO.COMPUTATIONAL_RESOURCES; |
| import static com.epam.datalab.backendapi.dao.ExploratoryDAO.exploratoryCondition; |
| import static com.epam.datalab.backendapi.dao.ExploratoryDAO.runningExploratoryAndComputationalCondition; |
| import static com.epam.datalab.backendapi.dao.MongoCollections.USER_INSTANCES; |
| import static com.mongodb.client.model.Filters.and; |
| import static com.mongodb.client.model.Filters.eq; |
| import static com.mongodb.client.model.Projections.elemMatch; |
| import static com.mongodb.client.model.Projections.excludeId; |
| import static com.mongodb.client.model.Projections.fields; |
| import static com.mongodb.client.model.Projections.include; |
| import static com.mongodb.client.model.Updates.push; |
| |
| /** |
| * DAO for user libraries. |
| */ |
| public class ExploratoryLibDAO extends BaseDAO { |
| public static final String EXPLORATORY_LIBS = "libs"; |
| public static final String COMPUTATIONAL_LIBS = "computational_libs"; |
| public static final String LIB_GROUP = "group"; |
| public static final String LIB_NAME = "name"; |
| public static final String LIB_VERSION = "version"; |
| public static final String LIB_AVAILABLE_VERSION = "available_versions"; |
| public static final String LIB_ADDED_PACKAGES = "add_pkgs"; |
| private static final String LIB_INSTALL_DATE = "install_date"; |
| private static final String LIB_ERROR_MESSAGE = "error_message"; |
| private static final String COMPUTATIONAL_NAME_FIELD = "computational_name"; |
| |
| /** |
| * Return condition for search library into exploratory data. |
| * |
| * @param libraryGroup the name of group. |
| * @param libraryName the name of library. |
| */ |
| private static Bson libraryConditionExploratory(String libraryGroup, String libraryName) { |
| return elemMatch(EXPLORATORY_LIBS, |
| libCondition(libraryGroup, libraryName)); |
| } |
| |
| |
| /** |
| * Return condition for search library into computational data. |
| * |
| * @param computationalName computational name |
| * @param libraryGroup the name of group. |
| * @param libraryName the name of library. |
| */ |
| private static Bson libraryConditionComputational(String computationalName, String libraryGroup, |
| String libraryName) { |
| return elemMatch(COMPUTATIONAL_LIBS + "." + computationalName, |
| and(eq(LIB_GROUP, libraryGroup), eq(LIB_NAME, libraryName))); |
| } |
| |
| /** |
| * Return field filter for libraries properties in exploratory data. |
| * |
| * @param fieldName |
| * @return |
| */ |
| private static String libraryFieldFilter(String fieldName) { |
| return EXPLORATORY_LIBS + FIELD_SET_DELIMETER + fieldName; |
| } |
| |
| |
| private static String computationalLibraryFieldFilter(String computational, String fieldName) { |
| return COMPUTATIONAL_LIBS + "." + computational + FIELD_SET_DELIMETER + fieldName; |
| } |
| |
| private Document findLibraries(String user, String project, String exploratoryName, Bson include) { |
| Optional<Document> opt = findOne(USER_INSTANCES, |
| exploratoryCondition(user, exploratoryName, project), |
| fields(excludeId(), include)); |
| |
| return opt.orElseGet(Document::new); |
| |
| } |
| |
| public List<Library> getLibraries(String user, String project, String exploratoryName) { |
| final Document libsDocument = findAllLibraries(user, project, exploratoryName); |
| return Stream |
| .concat( |
| libraryStream(libsDocument, exploratoryName, EXPLORATORY_LIBS, ResourceType.EXPLORATORY), |
| computationalLibStream(libsDocument)) |
| .collect(Collectors.toList()); |
| } |
| |
| public Document findAllLibraries(String user, String project, String exploratoryName) { |
| return findLibraries(user, project, exploratoryName, include(EXPLORATORY_LIBS, COMPUTATIONAL_LIBS, |
| COMPUTATIONAL_RESOURCES)); |
| } |
| |
| public Document findExploratoryLibraries(String user, String project, String exploratoryName) { |
| return findLibraries(user, project, exploratoryName, include(EXPLORATORY_LIBS)); |
| } |
| |
| public Document findComputationalLibraries(String user, String project, String exploratoryName, String computationalName) { |
| return findLibraries(user, project, exploratoryName, include(COMPUTATIONAL_LIBS + "." + computationalName)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Library getLibrary(String user, String project, String exploratoryName, String libraryGroup, String libraryName) { |
| Optional<Document> userInstance = findOne(USER_INSTANCES, |
| and(exploratoryCondition(user, exploratoryName, project), |
| elemMatch(EXPLORATORY_LIBS, |
| and(eq(LIB_GROUP, libraryGroup), eq(LIB_NAME, libraryName)) |
| )), |
| Projections.fields(excludeId(), Projections.include(EXPLORATORY_LIBS))); |
| |
| if (userInstance.isPresent()) { |
| final Object exloratoryLibs = userInstance.get().get(EXPLORATORY_LIBS); |
| List<Document> libs = exloratoryLibs != null ? (List<Document>) exloratoryLibs : Collections.emptyList(); |
| return libs.stream() |
| .filter(libraryPredicate(libraryGroup, libraryName)) |
| .map(d -> convertFromDocument(d, Library.class)) |
| .findAny().orElse(null); |
| |
| } |
| |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Library getLibrary(String user, String project, String exploratoryName, String computationalName, |
| String libraryGroup, String libraryName) { |
| Optional<Document> libraryStatus = findOne(USER_INSTANCES, |
| and(runningExploratoryAndComputationalCondition(user, project, exploratoryName, computationalName), |
| libraryConditionComputational(computationalName, libraryGroup, libraryName) |
| ), |
| |
| Projections.fields(excludeId(), |
| Projections.include( |
| COMPUTATIONAL_LIBS + "." + computationalName + "." + STATUS, |
| COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_GROUP, |
| COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_NAME) |
| ) |
| ); |
| |
| return libraryStatus.map(document -> ((List<Document>) (((Document) document.get(COMPUTATIONAL_LIBS)).get(computationalName))) |
| .stream() |
| .filter(libraryPredicate(libraryGroup, libraryName)) |
| .map(l -> convertFromDocument(l, Library.class)) |
| .findAny().orElse(null)).orElse(null); |
| } |
| |
| private Predicate<Document> libraryPredicate(String libraryGroup, String libraryName) { |
| return l -> libraryGroup.equals(l.getString(LIB_GROUP)) |
| && libraryName.equals(l.getString(LIB_NAME)); |
| } |
| |
| /** |
| * Add the user's library for exploratory into database. |
| * |
| * @param user user name. |
| * @param project project name |
| * @param exploratoryName name of exploratory. |
| * @param library library. |
| * @return <b>true</b> if operation was successful, otherwise <b>false</b>. |
| */ |
| public boolean addLibrary(String user, String project, String exploratoryName, LibInstallDTO library, boolean reinstall) { |
| Optional<Document> opt = findOne(USER_INSTANCES, |
| and(exploratoryCondition(user, exploratoryName, project), |
| elemMatch(EXPLORATORY_LIBS, |
| and(eq(LIB_GROUP, library.getGroup()), eq(LIB_NAME, library.getName()))))); |
| if (!opt.isPresent()) { |
| updateOne(USER_INSTANCES, |
| exploratoryCondition(user, exploratoryName, project), |
| push(EXPLORATORY_LIBS, convertToBson(library))); |
| return true; |
| } else { |
| Document values = addLibraryFields(library); |
| if (reinstall) { |
| values.append(libraryFieldFilter(LIB_INSTALL_DATE), null); |
| values.append(libraryFieldFilter(LIB_ERROR_MESSAGE), null); |
| } |
| |
| updateOne(USER_INSTANCES, and(exploratoryCondition(user, exploratoryName, project), |
| elemMatch(EXPLORATORY_LIBS, |
| and(eq(LIB_GROUP, library.getGroup()), eq(LIB_NAME, library.getName())))), |
| new Document(SET, values)); |
| return false; |
| } |
| } |
| |
| /** |
| * Add the user's library for exploratory into database. |
| * |
| * @param user user name. |
| * @param project project name |
| * @param exploratoryName name of exploratory. |
| * @param computationalName name of computational. |
| * @param library library. |
| * @return <b>true</b> if operation was successful, otherwise <b>false</b>. |
| */ |
| public boolean addLibrary(String user, String project, String exploratoryName, String computationalName, |
| LibInstallDTO library, boolean reinstall) { |
| |
| Optional<Document> opt = findOne(USER_INSTANCES, |
| and(runningExploratoryAndComputationalCondition(user, project, exploratoryName, computationalName), |
| eq(COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_GROUP, library.getGroup()), |
| eq(COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_NAME, library.getName()))); |
| |
| if (!opt.isPresent()) { |
| updateOne(USER_INSTANCES, |
| runningExploratoryAndComputationalCondition(user, project, exploratoryName, computationalName), |
| push(COMPUTATIONAL_LIBS + "." + computationalName, convertToBson(library))); |
| return true; |
| } else { |
| Document values = addComputationalLibraryFields(computationalName, library); |
| if (reinstall) { |
| values.append(computationalLibraryFieldFilter(computationalName, LIB_INSTALL_DATE), null); |
| values.append(computationalLibraryFieldFilter(computationalName, LIB_ERROR_MESSAGE), null); |
| } |
| |
| updateOne(USER_INSTANCES, and( |
| exploratoryCondition(user, exploratoryName, project), |
| eq(COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_GROUP, library.getGroup()), |
| eq(COMPUTATIONAL_LIBS + "." + computationalName + "." + LIB_NAME, library.getName())), |
| |
| new Document(SET, values)); |
| |
| return false; |
| } |
| } |
| |
| /** |
| * Updates the info about libraries for exploratory/computational in Mongo database. |
| * |
| * @param dto object of computational resource status. |
| */ |
| public void updateLibraryFields(LibInstallStatusDTO dto) { |
| if (dto.getLibs() == null) { |
| return; |
| } |
| |
| if (StringUtils.isEmpty(dto.getComputationalName())) { |
| updateExploratoryLibraryFields(dto); |
| } else { |
| updateComputationalLibraryFields(dto); |
| } |
| } |
| |
| private void updateExploratoryLibraryFields(LibInstallStatusDTO dto) { |
| for (LibInstallDTO lib : dto.getLibs()) { |
| try { |
| Document values = updateLibraryFields(lib, dto.getUptime()); |
| |
| updateOne(USER_INSTANCES, |
| and(exploratoryCondition(dto.getUser(), dto.getExploratoryName(), dto.getProject()), |
| libraryConditionExploratory(lib.getGroup(), lib.getName())), |
| new Document(SET, values)); |
| } catch (Exception e) { |
| throw new DatalabException(String.format("Could not update library %s for %s", |
| lib, dto.getExploratoryName()), e); |
| } |
| } |
| } |
| |
| private void updateComputationalLibraryFields(LibInstallStatusDTO dto) { |
| for (LibInstallDTO lib : dto.getLibs()) { |
| try { |
| Document values = updateComputationalLibraryFields(dto.getComputationalName(), lib, dto.getUptime()); |
| |
| updateOne(USER_INSTANCES, |
| and(exploratoryCondition(dto.getUser(), dto.getExploratoryName(), dto.getProject()), |
| elemMatch(COMPUTATIONAL_LIBS + "." + dto.getComputationalName(), |
| libCondition(lib.getGroup(), lib.getName()))), |
| new Document(SET, values)); |
| } catch (Exception e) { |
| throw new DatalabException(String.format("Could not update library %s for %s/%s", |
| lib, dto.getExploratoryName(), dto.getComputationalName()), e); |
| } |
| } |
| } |
| |
| private static Bson libCondition(String group, String name) { |
| return and(eq(LIB_GROUP, group), eq(LIB_NAME, name)); |
| } |
| |
| private Document addLibraryFields(LibInstallDTO lib) { |
| Document values = new Document(libraryFieldFilter(STATUS), lib.getStatus()); |
| if (lib.getVersion() != null) { |
| values.append(libraryFieldFilter(LIB_VERSION), lib.getVersion()); |
| } |
| |
| return values; |
| } |
| |
| private Document updateLibraryFields(LibInstallDTO lib, Date uptime) { |
| Document values = new Document(libraryFieldFilter(STATUS), lib.getStatus()); |
| if (lib.getVersion() != null) { |
| values.append(libraryFieldFilter(LIB_VERSION), lib.getVersion()); |
| } |
| if (uptime != null) { |
| values.append(libraryFieldFilter(LIB_INSTALL_DATE), uptime); |
| } |
| if (lib.getAvailableVersions() != null) { |
| values.append(libraryFieldFilter(LIB_AVAILABLE_VERSION), lib.getAvailableVersions()); |
| } |
| if (lib.getAddedPackages() != null) { |
| values.append(libraryFieldFilter(LIB_ADDED_PACKAGES), lib.getAddedPackages()); |
| } |
| if (lib.getErrorMessage() != null) { |
| values.append(libraryFieldFilter(LIB_ERROR_MESSAGE), |
| DateRemoverUtil.removeDateFormErrorMessage(lib.getErrorMessage())); |
| } |
| |
| return values; |
| } |
| |
| private Document addComputationalLibraryFields(String computational, LibInstallDTO lib) { |
| Document values = new Document(computationalLibraryFieldFilter(computational, STATUS), lib.getStatus()); |
| if (lib.getVersion() != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_VERSION), lib.getVersion()); |
| } |
| |
| return values; |
| } |
| |
| private Document updateComputationalLibraryFields(String computational, LibInstallDTO lib, Date uptime) { |
| Document values = new Document(computationalLibraryFieldFilter(computational, STATUS), lib.getStatus()); |
| if (lib.getVersion() != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_VERSION), lib.getVersion()); |
| } |
| if (uptime != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_INSTALL_DATE), uptime); |
| } |
| if (lib.getAvailableVersions() != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_AVAILABLE_VERSION), lib.getAvailableVersions()); |
| } |
| if (lib.getAddedPackages() != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_ADDED_PACKAGES), lib.getAddedPackages()); |
| } |
| if (lib.getErrorMessage() != null) { |
| values.append(computationalLibraryFieldFilter(computational, LIB_ERROR_MESSAGE), |
| DateRemoverUtil.removeDateFormErrorMessage(lib.getErrorMessage())); |
| } |
| |
| return values; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Stream<Library> computationalLibStream(Document libsDocument) { |
| return ((List<Document>) libsDocument.getOrDefault(COMPUTATIONAL_RESOURCES, Collections.emptyList())) |
| .stream() |
| .map(d -> d.getString(COMPUTATIONAL_NAME_FIELD)) |
| .flatMap(compName -> libraryStream( |
| (Document) libsDocument.getOrDefault(COMPUTATIONAL_LIBS, new Document()), |
| compName, |
| compName, ResourceType.COMPUTATIONAL)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Stream<Library> libraryStream(Document libsDocument, String resourceName, String libFieldName, |
| ResourceType libType) { |
| return ((List<Document>) libsDocument.getOrDefault(libFieldName, Collections.emptyList())) |
| .stream() |
| .map(d -> convertFromDocument(d, Library.class)) |
| .filter(library -> !Arrays.asList(LibStatus.INVALID_VERSION, LibStatus.INVALID_NAME).contains(library.getStatus())) |
| .peek(l -> l.withType(libType).withResourceName(resourceName)); |
| } |
| } |