From bc6378d695446860a9387f9aa62080d70f5f3161 Mon Sep 17 00:00:00 2001 From: Lars Johansson <lars.johansson@ess.eu> Date: Wed, 29 Mar 2023 11:37:38 +0200 Subject: [PATCH] Add endpoint to get legacy names --- .../names/repository/NameRepository.java | 130 ++++++++++++++++++ .../openepics/names/rest/api/v1/INames.java | 43 ++++++ .../rest/controller/NamesController.java | 18 +++ .../openepics/names/service/NamesService.java | 29 ++++ 4 files changed, 220 insertions(+) diff --git a/src/main/java/org/openepics/names/repository/NameRepository.java b/src/main/java/org/openepics/names/repository/NameRepository.java index 14f1eb81..b949d580 100644 --- a/src/main/java/org/openepics/names/repository/NameRepository.java +++ b/src/main/java/org/openepics/names/repository/NameRepository.java @@ -23,6 +23,7 @@ import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; @@ -240,6 +241,135 @@ public class NameRepository { return query.getResultList(); } + /** + * Count legacy names + * + * @param name name + * @return count of legacy names + */ + public Long countNamesLegacy(String name) { + // a name is considered legacy if it is active but refers to a parent that is deleted + // query + // logical but non-trivial + // relies on lifecycle and parent-child attributes + // look for one or more deleted parents anywhere in system structure or device structure hierarchies + StringBuilder sql = new StringBuilder(); + sql.append("select count(n) from Name n where n.latest = true and n.deleted = false "); + sql.append("and ("); + sql.append(" (n.systemGroupUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true))"); + sql.append(" or (n.systemUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.deleted = true))"); + sql.append(" or (n.systemUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.parentUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true)))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.deleted = true))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.parentUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.deleted = true)))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.parentUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.parentUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true))))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.deleted = true))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.parentUuid in (select dg.uuid from DeviceGroup dg where dg.status = 'APPROVED' and dg.latest = true and dg.deleted = true)))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.parentUuid in (select dg.uuid from DeviceGroup dg where dg.status = 'APPROVED' and dg.latest = true and dg.parentUuid in (select di.uuid from Discipline di where di.status = 'APPROVED' and di.latest = true and di.deleted = true))))"); + sql.append(")"); + + boolean hasWhere = StringUtils.isNotEmpty(name); + Query query = null; + if (hasWhere) { + sql.append(" and n.conventionName like :pName"); + query = em.createQuery(sql.toString(), Long.class).setParameter("pName", name); + } else { + query = em.createQuery(sql.toString(), Long.class); + } + + return (Long) query.getSingleResult(); + } + + /** + * Find legacy names. + * + * @param name name + * @param orderBy order by + * @param isAsc is ascending + * @param offset offset + * @param limit limit + * @return list of names + * @return + */ + @SuppressWarnings("unchecked") + public List<Name> readNamesLegacy(String name, + FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) { + + // a name is considered legacy if it is active but refers to a parent that is deleted + // query + // logical but non-trivial + // relies on lifecycle and parent-child attributes + // look for one or more deleted parents anywhere in system structure or device structure hierarchies + StringBuilder sql = new StringBuilder(); + sql.append("select n from Name n where n.latest = true and n.deleted = false "); + sql.append("and ("); + sql.append(" (n.systemGroupUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true))"); + sql.append(" or (n.systemUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.deleted = true))"); + sql.append(" or (n.systemUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.parentUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true)))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.deleted = true))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.parentUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.deleted = true)))"); + sql.append(" or (n.subsystemUuid in (select su.uuid from Subsystem su where su.status = 'APPROVED' and su.latest = true and su.parentUuid in (select sy.uuid from System sy where sy.status = 'APPROVED' and sy.latest = true and sy.parentUuid in (select sg.uuid from SystemGroup sg where sg.status = 'APPROVED' and sg.latest = true and sg.deleted = true))))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.deleted = true))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.parentUuid in (select dg.uuid from DeviceGroup dg where dg.status = 'APPROVED' and dg.latest = true and dg.deleted = true)))"); + sql.append(" or (n.deviceTypeUuid in (select dt.uuid from DeviceType dt where dt.status = 'APPROVED' and dt.latest = true and dt.parentUuid in (select dg.uuid from DeviceGroup dg where dg.status = 'APPROVED' and dg.latest = true and dg.parentUuid in (select di.uuid from Discipline di where di.status = 'APPROVED' and di.latest = true and di.deleted = true))))"); + sql.append(")"); + + StringBuilder sqlOrderBy = new StringBuilder(); + if (orderBy != null) { + sqlOrderBy.append(" order by "); + + if (FieldName.NAMEEQUIVALENCE.equals(orderBy)) { + sqlOrderBy.append("n."); + sqlOrderBy.append(Name.FIELD_CONVENTION_NAME_EQUIVALENCE); + } else if (FieldName.SYSTEMSTRUCTURE.equals(orderBy)) { + sqlOrderBy.append(NameStructure.FUNCTION_GET_MNEMONIC_PATH_SYSTEM_STRUCTURE); + sqlOrderBy.append("(n.conventionName)"); + } else if (FieldName.DEVICESTRUCTURE.equals(orderBy)) { + sqlOrderBy.append(NameStructure.FUNCTION_GET_MNEMONIC_PATH_DEVICE_STRUCTURE); + sqlOrderBy.append("(n.conventionName)"); + } else if (FieldName.INDEX.equals(orderBy)) { + sqlOrderBy.append(NameStructure.FUNCTION_GET_INSTANCE_INDEX); + sqlOrderBy.append("(n.conventionName)"); + } else if (FieldName.DESCRIPTION.equals(orderBy)) { + sqlOrderBy.append("n."); + sqlOrderBy.append(Name.FIELD_DESCRIPTION); + } else if (FieldName.WHEN.equals(orderBy)) { + sqlOrderBy.append("n."); + sqlOrderBy.append(Name.FIELD_REQUESTED); + } else { + sqlOrderBy.append("n."); + sqlOrderBy.append(Name.FIELD_CONVENTION_NAME); + } + + if (BooleanUtils.toBoolean(isAsc)) { + sqlOrderBy.append(" asc"); + } else { + sqlOrderBy.append(" desc"); + } + } + + boolean hasWhere = StringUtils.isNotEmpty(name); + boolean hasOrderBy = orderBy != null; + Query query = null; + if (hasWhere) { + sql.append(" and n.conventionName like :pName"); + if (hasOrderBy) { + sql.append(sqlOrderBy.toString()); + } + query = em.createQuery(sql.toString(), Name.class).setParameter("pName", name); + } else { + if (hasOrderBy) { + sql.append(sqlOrderBy.toString()); + } + query = em.createQuery(sql.toString(), Name.class); + } + + if (offset != null && limit != null) { + query.setFirstResult(offset * limit); + query.setMaxResults(limit); + } + return query.getResultList(); + } + /** * Prepare predicates for names. * diff --git a/src/main/java/org/openepics/names/rest/api/v1/INames.java b/src/main/java/org/openepics/names/rest/api/v1/INames.java index 8742dc74..a4f0afad 100644 --- a/src/main/java/org/openepics/names/rest/api/v1/INames.java +++ b/src/main/java/org/openepics/names/rest/api/v1/INames.java @@ -636,6 +636,49 @@ public interface INames { @Parameter(in = ParameterIn.QUERY, description = "page starting from 0, offset") @RequestParam(required = false, defaultValue = DEFAULT_PAGE) Integer page, @Parameter(in = ParameterIn.QUERY, description = "page size, limit") @RequestParam(required = false, defaultValue = DEFAULT_PAGE_SIZE) Integer pageSize); + @Operation( + summary = "Find valid legacy names (search)", + description = """ + Find valid legacy names (search). + Return paged array of name elements. + """ + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "OK. Return paged array of name elements.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ResponsePageNameElements.class))), + @ApiResponse( + responseCode = "400", + description = "Bad request. Reason and information such as message, details, field are available.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Response.class))), + @ApiResponse( + responseCode = "422", + description = "Unprocessable entity. Reason and information such as message, details, field are available.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Response.class))), + @ApiResponse( + responseCode = "500", + description = "Internal server error. Reason and information such as message, details, field are available.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Response.class))) + }) + @GetMapping( + value = "/legacy", + produces = {"application/json"}) + public ResponsePageNameElements readNamesLegacy( + @Parameter(in = ParameterIn.QUERY, description = "search by name") @RequestParam(required = false) String name, + @Parameter(in = ParameterIn.QUERY, description = "order by field") @RequestParam(required = false, defaultValue = DEFAULT_SORT_FIELD_WHEN) FieldName orderBy, + @Parameter(in = ParameterIn.QUERY, description = "sort order, ascending or descending") @RequestParam(required = false, defaultValue = DEFAULT_SORT_ORDER_ASC) Boolean isAsc, + @Parameter(in = ParameterIn.QUERY, description = "page starting from 0, offset") @RequestParam(required = false, defaultValue = DEFAULT_PAGE) Integer page, + @Parameter(in = ParameterIn.QUERY, description = "page size, limit") @RequestParam(required = false, defaultValue = DEFAULT_PAGE_SIZE) Integer pageSize); + // ---------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/org/openepics/names/rest/controller/NamesController.java b/src/main/java/org/openepics/names/rest/controller/NamesController.java index 9b1cc613..784f2c98 100644 --- a/src/main/java/org/openepics/names/rest/controller/NamesController.java +++ b/src/main/java/org/openepics/names/rest/controller/NamesController.java @@ -274,6 +274,24 @@ public class NamesController implements INames { } } + @Override + public ResponsePageNameElements readNamesLegacy(String name, + FieldName orderBy, Boolean isAsc, Integer page, Integer pageSize) { + // not validate + // read names + try { + return namesService.readNamesLegacy(name, + orderBy, isAsc, page, pageSize); + } catch (ServiceException e) { + logService.logServiceException(LOGGER, Level.WARNING, e); + logService.logStackTraceElements(LOGGER, Level.WARNING, e); + throw e; + } catch (Exception e) { + logService.logStackTraceElements(LOGGER, Level.WARNING, e); + throw e; + } + } + // ---------------------------------------------------------------------------------------------------- @Override diff --git a/src/main/java/org/openepics/names/service/NamesService.java b/src/main/java/org/openepics/names/service/NamesService.java index 6a6751f3..e69af587 100644 --- a/src/main/java/org/openepics/names/service/NamesService.java +++ b/src/main/java/org/openepics/names/service/NamesService.java @@ -336,6 +336,35 @@ public class NamesService { Boolean.TRUE, orderBy, isAsc, offset, limit); } + public ResponsePageNameElements readNamesLegacy(String name, + FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) { + // validation outside method + // read legacy names + // return name elements for names + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.READ_NAMES, "name", name)); + LOGGER.log(Level.FINE, MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.READ_NAMES, "orderBy", orderBy)); + LOGGER.log(Level.FINE, MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.READ_NAMES, "isAsc", isAsc)); + LOGGER.log(Level.FINE, MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.READ_NAMES, "offset", offset)); + LOGGER.log(Level.FINE, MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.READ_NAMES, "limit", limit)); + } + + List<Name> names = nameRepository.readNamesLegacy(name, orderBy, isAsc, offset, limit); + Long totalCount = nameRepository.countNamesLegacy(name); + + final List<NameElement> nameElements = NameElementUtil.getNameElements(names); + + ResponsePageNameElements response = new ResponsePageNameElements(nameElements, totalCount, nameElements.size(), offset, limit); + LOGGER.log(Level.INFO, + () -> MessageFormat.format( + TextUtil.DESCRIPTION_NUMBER_ELEMENTS, + TextUtil.READ_NAMES, + nameElements.size())); + return response; + + } + // ---------------------------------------------------------------------------------------------------- public String equivalenceName(String name) { -- GitLab