From 7f668471021ca9b3a329aea3d3902aecb2825e46 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 12 Aug 2016 16:29:28 +0200 Subject: [PATCH 01/49] Various small code improvements --- .../postgresql/PostgreSqlEntityFactory.java | 6 +- .../PostgreSqlExceptionTranslator.java | 25 ++---- .../postgresql/PostgreSqlQueryGenerator.java | 90 ++++++++++++------- .../data/postgresql/PostgreSqlQueryUtils.java | 20 ++--- .../data/postgresql/PostgreSqlRepository.java | 16 +--- .../PostgreSqlRepositoryCollection.java | 35 ++++---- 6 files changed, 102 insertions(+), 90 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlEntityFactory.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlEntityFactory.java index caa4cbcb0ab..84b29915b57 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlEntityFactory.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlEntityFactory.java @@ -86,7 +86,7 @@ public Entity mapRow(ResultSet resultSet, int i) throws SQLException * @param resultSet result set * @param attr attribute * @return value for the given attribute in the type defined by the attribute type - * @throws SQLException + * @throws SQLException if an error occurs reading from the result set */ private Object mapValue(ResultSet resultSet, AttributeMetaData attr) throws SQLException { @@ -101,7 +101,7 @@ private Object mapValue(ResultSet resultSet, AttributeMetaData attr) throws SQLE * @param attr attribute * @param colName column name in the result set * @return value for the given attribute in the type defined by the attribute type - * @throws SQLException + * @throws SQLException if an error occurs reading from the result set */ private Object mapValue(ResultSet resultSet, AttributeMetaData attr, String colName) throws SQLException { @@ -169,7 +169,7 @@ private Object mapValue(ResultSet resultSet, AttributeMetaData attr, String colN * @param arrayValue result set array value * @param entityMeta entity meta data * @return mapped value - * @throws SQLException + * @throws SQLException if an error occurs while attempting to access the array */ private Object mapValueMref(Array arrayValue, EntityMetaData entityMeta) throws SQLException { diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java index 8529e691e5f..d635b267870 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java @@ -73,15 +73,8 @@ private MolgenisDataException doTranslate(SQLException sqlException) return molgenisDataException; } - private MolgenisDataException doTranslate(PSQLException sqlException) + private MolgenisDataException doTranslate(PSQLException pSqlException) { - if (!(sqlException instanceof PSQLException)) - { - throw new RuntimeException( - format("Unexpected exception class [%s]", sqlException.getClass().getSimpleName())); - } - - PSQLException pSqlException = sqlException; switch (pSqlException.getSQLState()) { case "22007": // invalid_datetime_format @@ -101,8 +94,8 @@ private MolgenisDataException doTranslate(PSQLException sqlException) /** * Package private for testability * - * @param pSqlException - * @return + * @param pSqlException PostgreSQL exception + * @return translated validation exception */ MolgenisValidationException translateInvalidIntegerException(PSQLException pSqlException) { @@ -149,8 +142,8 @@ MolgenisValidationException translateInvalidIntegerException(PSQLException pSqlE /** * Package private for testability * - * @param pSqlException - * @return + * @param pSqlException PostgreSQL exception + * @return translated validation exception */ MolgenisValidationException translateNotNullViolation(PSQLException pSqlException) { @@ -189,8 +182,8 @@ MolgenisValidationException translateNotNullViolation(PSQLException pSqlExceptio /** * Package private for testability * - * @param pSqlException - * @return + * @param pSqlException PostgreSQL exception + * @return translated validation exception */ MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlException) { @@ -216,8 +209,8 @@ MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlExcep /** * Package private for testability * - * @param pSqlException - * @return + * @param pSqlException PostgreSQL exception + * @return translated validation exception */ MolgenisValidationException translateUniqueKeyViolation(PSQLException pSqlException) { diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index 93f0d28f727..283bae3bc8a 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -1,13 +1,11 @@ package org.molgenis.data.postgresql; -import org.molgenis.MolgenisFieldTypes; import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.*; import org.molgenis.data.QueryRule.Operator; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.data.support.QueryImpl; -import org.molgenis.fieldtypes.FieldType; import java.text.MessageFormat; import java.util.ArrayList; @@ -20,7 +18,8 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.IntStream.range; import static java.util.stream.StreamSupport.stream; -import static org.molgenis.MolgenisFieldTypes.AttributeType.*; +import static org.molgenis.MolgenisFieldTypes.AttributeType.BOOL; +import static org.molgenis.MolgenisFieldTypes.AttributeType.ENUM; import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.*; import static org.molgenis.data.support.EntityMetaDataUtils.*; @@ -116,7 +115,7 @@ static String getSqlCreateTable(EntityMetaData entityMeta) getColumnName(idAttribute))); } - if (idAttribute.isNillable() == true) + if (idAttribute.isNillable()) { throw new RuntimeException(format("idAttribute (%s.%s) should not be nillable", getTableName(entityMeta), getColumnName(idAttribute))); @@ -172,9 +171,9 @@ static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMeta static String getSqlCreateJunctionTableIndex(EntityMetaData entityMeta, AttributeMetaData attr) { String junctionTableName = getJunctionTableName(entityMeta, attr); - return "CREATE INDEX " + junctionTableName.substring(0, junctionTableName.length() - 1) + "_" + entityMeta + return "CREATE INDEX " + junctionTableName.substring(0, junctionTableName.length() - 1) + '_' + entityMeta .getIdAttribute().getName() + "_index\" ON " + junctionTableName + " (" + getColumnName( - entityMeta.getIdAttribute()) + ")"; + entityMeta.getIdAttribute()) + ')'; } static String getSqlDropJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) @@ -208,7 +207,7 @@ static String getSqlInsert(EntityMetaData entityMeta) sql.setLength(sql.length() - 2); params.setLength(params.length() - 2); } - sql.append(") VALUES (").append(params).append(")"); + sql.append(") VALUES (").append(params).append(')'); return sql.toString(); } @@ -241,7 +240,7 @@ static String getJunctionTableSelect(EntityMetaData entityMeta, AttributeMetaDat + getColumnName(attr) + " FROM " + getJunctionTableName(entityMeta, attr) + " WHERE " + getColumnName( entityMeta.getIdAttribute()) + " in (" + range(0, numOfIds).mapToObj((x) -> "?").collect(joining(", ")) + ") ORDER BY " + getColumnName(entityMeta.getIdAttribute()) + ", \"" + JUNCTION_TABLE_ORDER_ATTR_NAME - + "\""; + + '"'; } static String getSqlSelect(EntityMetaData entityMeta, Query q, List parameters, @@ -324,10 +323,7 @@ static String getSqlUpdate(EntityMetaData entityMeta) // create sql StringBuilder sql = new StringBuilder("UPDATE ").append(getTableName(entityMeta)).append(" SET "); - getPersistedAttributesNonMref(entityMeta).forEach(attr -> - { - sql.append(getColumnName(attr)).append(" = ?, "); - }); + getPersistedAttributesNonMref(entityMeta).forEach(attr -> sql.append(getColumnName(attr)).append(" = ?, ")); if (sql.charAt(sql.length() - 1) == ' ' && sql.charAt(sql.length() - 2) == ',') { sql.setLength(sql.length() - 2); @@ -339,9 +335,9 @@ static String getSqlUpdate(EntityMetaData entityMeta) /** * Produces SQL to count the number of entities that match the given query. Ignores query offset and pagesize. * - * @param q - * @param parameters - * @return + * @param q query + * @param parameters prepared statement parameters + * @return SQL string */ static String getSqlCount(EntityMetaData entityMeta, Query q, List parameters) { @@ -475,7 +471,7 @@ private static String getSqlWhere(EntityMetaData entityMeta, if (isStringType(attr) || isTextType(attr)) { - result.append(" ").append(columnName); + result.append(' ').append(columnName); } else { @@ -483,7 +479,7 @@ private static String getSqlWhere(EntityMetaData entityMeta, } result.append(" LIKE ?"); - parameters.add("%" + PostgreSqlUtils.getPostgreSqlQueryValue(r.getValue(), attr) + "%"); + parameters.add("%" + PostgreSqlUtils.getPostgreSqlQueryValue(r.getValue(), attr) + '%'); break; case IN: Object inValue = r.getValue(); @@ -537,7 +533,7 @@ private static String getSqlWhere(EntityMetaData entityMeta, throw new MolgenisDataException(format("RANGE value is of type [%s] instead of [Iterable]", range.getClass().getSimpleName())); } - Iterator rangeValues = ((Iterable) range).iterator(); + Iterator rangeValues = ((Iterable) range).iterator(); parameters.add(rangeValues.next()); // from parameters.add(rangeValues.next()); // to @@ -612,9 +608,6 @@ private static String getSqlWhere(EntityMetaData entityMeta, predicate.append('.').append(getColumnName(r.getField())); switch (operator) { - case EQUALS: - predicate.append(" ="); - break; case GREATER: predicate.append(" >"); break; @@ -734,26 +727,26 @@ private static String getSqlFromForCount(EntityMetaData entit return from.toString(); } - static String getColumnName(AttributeMetaData attr) + private static String getColumnName(AttributeMetaData attr) { return getColumnName(attr.getName()); } - static String getColumnName(String attrName) + private static String getColumnName(String attrName) { - return new StringBuilder().append("\"").append(attrName).append("\"").toString(); + return new StringBuilder().append('"').append(attrName).append('"').toString(); } private static String getFilterColumnName(AttributeMetaData attr, int filterIndex) { - return new StringBuilder().append("\"").append(attr.getName()).append("_filter").append(filterIndex) - .append("\"").toString(); + return new StringBuilder().append('"').append(attr.getName()).append("_filter").append(filterIndex).append('"') + .toString(); } private static String getUniqueKeyName(EntityMetaData emd, AttributeMetaData attr) { - return new StringBuilder().append("\"").append(emd.getName()).append('_').append(attr.getName()).append("_key") - .append("\"").toString(); + return new StringBuilder().append('"').append(emd.getName()).append('_').append(attr.getName()).append("_key") + .append('"').toString(); } private static List getMrefQueryAttrs(EntityMetaData entityMeta, Query q) @@ -786,8 +779,45 @@ private static void getMrefQueryFieldsRec(EntityMetaData entityMeta, List getPersistedAttributes(EntityMetaData entityMeta) { @@ -82,7 +82,7 @@ public static Stream getPersistedAttributes(EntityMetaData en * Returns all MREF attributes persisted by PostgreSQL (e.g. no compound attributes and attributes with an * expression) * - * @return + * @return stream of persisted MREF attributes */ public static Stream getPersistedAttributesMref(EntityMetaData entityMeta) { @@ -93,7 +93,7 @@ public static Stream getPersistedAttributesMref(EntityMetaDat * Returns all non-MREF attributes persisted by PostgreSQL (e.g. no compound attributes and attributes with an * expression) * - * @return + * @return stream of persisted non-MREF attributes */ public static Stream getPersistedAttributesNonMref(EntityMetaData entityMeta) { diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepository.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepository.java index fce614e55e3..7369346e278 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepository.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepository.java @@ -37,7 +37,6 @@ import static com.google.common.base.Stopwatch.createStarted; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; -import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; @@ -70,9 +69,9 @@ public class PostgreSqlRepository extends AbstractRepository /** * Repository capabilities */ - private static final Set REPO_CAPABILITIES = unmodifiableSet(new HashSet<>( - asList(WRITABLE, MANAGABLE, QUERYABLE, VALIDATE_REFERENCE_CONSTRAINT, VALIDATE_UNIQUE_CONSTRAINT, - VALIDATE_NOTNULL_CONSTRAINT, CACHEABLE))); + private static final Set REPO_CAPABILITIES = unmodifiableSet( + EnumSet.of(WRITABLE, MANAGABLE, QUERYABLE, VALIDATE_REFERENCE_CONSTRAINT, VALIDATE_UNIQUE_CONSTRAINT, + VALIDATE_NOTNULL_CONSTRAINT, CACHEABLE)); /** * Supported query operators @@ -542,7 +541,7 @@ public void setValues(PreparedStatement preparedStatement, int rowIndex) throws preparedStatement.setObject(fieldIndex++, postgreSqlValue); } - preparedStatement.setObject(fieldIndex++, PostgreSqlUtils.getPostgreSqlValue(entity, idAttr)); + preparedStatement.setObject(fieldIndex, PostgreSqlUtils.getPostgreSqlValue(entity, idAttr)); } @Override @@ -656,13 +655,6 @@ public int getBatchSize() }); } - @Deprecated - private static Object convert(AttributeMetaData attr, Object value) - { - FieldType fieldType = MolgenisFieldTypes.getType(getValueString(attr.getDataType())); - return fieldType.convert(value); - } - @Deprecated private static Object convert(AttributeType attrType, Object value) { diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 09b9624d960..711bb37b29e 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -87,10 +87,7 @@ public Stream getLanguageCodes() LOG.trace("SQL: {}", sql); } } - return jdbcTemplate.query(sql, (rs, rowNum) -> - { - return rs.getString(CODE); - }).stream(); + return jdbcTemplate.query(sql, (rs, rowNum) -> rs.getString(CODE)).stream(); } else { @@ -160,7 +157,7 @@ public void deleteRepository(EntityMetaData entityMeta) String sqlDropJunctionTable = getSqlDropJunctionTable(entityMeta, mrefAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping junction table for entity [{}] attribute [{}]", getName(), mrefAttr.getName()); + LOG.debug("Dropping junction table for entity [{}] attribute [{}]", POSTGRESQL, mrefAttr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", sqlDropJunctionTable); @@ -172,7 +169,7 @@ public void deleteRepository(EntityMetaData entityMeta) String sqlDropTable = getSqlDropTable(entityMeta); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping table for entity [{}]", getName()); + LOG.debug("Dropping table for entity [{}]", POSTGRESQL); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", sqlDropTable); @@ -185,23 +182,23 @@ public void deleteRepository(EntityMetaData entityMeta) public void addAttribute(String entityName, AttributeMetaData attribute) { EntityMetaData entityMetaData = dataService.getEntityMetaData(entityName); - if (null != entityMetaData.getAttribute(requireNonNull(attribute).getName())) - { - throw new MolgenisDataException( - format("Adding attribute operation failed. Attribute already exists [%s]", attribute.getName())); - } if (entityMetaData == null) { throw new UnknownEntityException( format("Adding attribute operation failed. Unknown entity [%s]", entityName)); } + if (null != entityMetaData.getAttribute(requireNonNull(attribute).getName())) + { + throw new MolgenisDataException( + format("Adding attribute operation failed. Attribute already exists [%s]", attribute.getName())); + } addAttributeRec(entityMetaData, attribute); } /** * Adds an attribute to the repository. * - * @param entityMetaData + * @param entityMetaData entity meta data that owns the attribute * @param attr the {@link AttributeMetaData} to add */ private void addAttributeRec(EntityMetaData entityMetaData, AttributeMetaData attr) @@ -217,7 +214,7 @@ private void addAttributeRec(EntityMetaData entityMetaData, AttributeMetaData at String createJunctionTableSql = getSqlCreateJunctionTable(entityMetaData, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating junction table for entity [{}] attribute [{}]", getName(), attr.getName()); + LOG.debug("Creating junction table for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createJunctionTableSql); @@ -230,7 +227,7 @@ else if (attr.getDataType() != COMPOUND) String addColumnSql = getSqlAddColumn(entityMetaData, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating column for entity [{}] attribute [{}]", getName(), attr.getName()); + LOG.debug("Creating column for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", addColumnSql); @@ -245,7 +242,7 @@ else if (attr.getDataType() != COMPOUND) String createForeignKeySql = getSqlCreateForeignKey(entityMetaData, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating foreign key for entity [{}] attribute [{}]", getName(), attr.getName()); + LOG.debug("Creating foreign key for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createForeignKeySql); @@ -260,7 +257,7 @@ else if (attr.getDataType() != COMPOUND) String createUniqueKeySql = getSqlCreateUniqueKey(entityMetaData, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating unique key for entity [{}] attribute [{}]", getName(), attr.getName()); + LOG.debug("Creating unique key for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createUniqueKeySql); @@ -378,7 +375,7 @@ private void deleteAttribute(EntityMetaData entityMetaData, String attrName) String dropColumnSql = getSqlDropColumn(entityMetaData, attrName); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping column for entity [{}] attribute [{}]", getName(), attrName); + LOG.debug("Dropping column for entity [{}] attribute [{}]", POSTGRESQL, attrName); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", dropColumnSql); @@ -388,9 +385,9 @@ private void deleteAttribute(EntityMetaData entityMetaData, String attrName) } /** - * Return a spring managed prototype bean + * Return a new PostgreSQL repository */ - protected PostgreSqlRepository createPostgreSqlRepository() + private PostgreSqlRepository createPostgreSqlRepository() { return new PostgreSqlRepository(postgreSqlEntityFactory, jdbcTemplate, dataSource, transactionManager); } From a073d4dcde393c659de653141c0569c37ceec33c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Mon, 15 Aug 2016 08:27:45 +0200 Subject: [PATCH 02/49] ENH add getDropForeignKey for an entity attribute --- .../postgresql/PostgreSqlQueryGenerator.java | 31 ++++++++++--- .../PostgreSqlQueryGeneratorTest.java | 43 +++++++++++++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index 283bae3bc8a..fbcff7db654 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -36,9 +36,10 @@ private PostgreSqlQueryGenerator() static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) { StringBuilder strBuilder = new StringBuilder(); - strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD FOREIGN KEY (") - .append(getColumnName(attr)).append(") REFERENCES ").append(getTableName(attr.getRefEntity())) - .append('(').append(getColumnName(attr.getRefEntity().getIdAttribute())).append(')'); + strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD CONSTRAINT ") + .append(getForeignKeyName(entityMeta, attr)).append(" FOREIGN KEY (").append(getColumnName(attr)) + .append(") REFERENCES ").append(getTableName(attr.getRefEntity())).append('(') + .append(getColumnName(attr.getRefEntity().getIdAttribute())).append(')'); // for self-referencing data defer checking constraints until the end of the transaction if (attr.getRefEntity().getName().equals(entityMeta.getName())) @@ -48,6 +49,14 @@ static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaDat return strBuilder.toString(); } + static String getSqlDropForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + StringBuilder strBuilder = new StringBuilder(); + strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP CONSTRAINT ") + .append(getForeignKeyName(entityMeta, attr)); + return strBuilder.toString(); + } + static String getSqlCreateUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { // PostgreSQL name convention @@ -743,10 +752,20 @@ private static String getFilterColumnName(AttributeMetaData attr, int filterInde .toString(); } - private static String getUniqueKeyName(EntityMetaData emd, AttributeMetaData attr) + private static String getForeignKeyName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getKeyName(entityMeta, attr, "fkey"); + } + + private static String getUniqueKeyName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getKeyName(entityMeta, attr, "key"); + } + + private static String getKeyName(EntityMetaData entityMeta, AttributeMetaData attr, String keyPostfix) { - return new StringBuilder().append('"').append(emd.getName()).append('_').append(attr.getName()).append("_key") - .append('"').toString(); + return new StringBuilder().append('"').append(entityMeta.getName()).append('_').append(attr.getName()) + .append('_').append(keyPostfix).append('"').toString(); } private static List getMrefQueryAttrs(EntityMetaData entityMeta, Query q) diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index 40dfc6d623c..6a62b697740 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -27,7 +27,6 @@ public class PostgreSqlQueryGeneratorTest @Test public void getSqlCreateForeignKey() { - AttributeMetaData refIdAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr").getMock(); EntityMetaData refEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity").getMock(); when(refEntityMeta.getIdAttribute()).thenReturn(refIdAttr); @@ -37,7 +36,7 @@ public void getSqlCreateForeignKey() when(refAttr.getDataType()).thenReturn(XREF); when(refAttr.getRefEntity()).thenReturn(refEntityMeta); - String expectedSql = "ALTER TABLE \"entity\" ADD FOREIGN KEY (\"attr\") REFERENCES \"refEntity\"(\"refIdAttr\")"; + String expectedSql = "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity\"(\"refIdAttr\")"; assertEquals(PostgreSqlQueryGenerator.getSqlCreateForeignKey(entityMeta, refAttr), expectedSql); } @@ -53,10 +52,48 @@ public void getSqlCreateForeignKeySelfReferencing() when(refAttr.getDataType()).thenReturn(XREF); when(refAttr.getRefEntity()).thenReturn(entityMeta); - String expectedSql = "ALTER TABLE \"entity\" ADD FOREIGN KEY (\"attr\") REFERENCES \"entity\"(\"idAttr\") DEFERRABLE INITIALLY DEFERRED"; + String expectedSql = "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"entity\"(\"idAttr\") DEFERRABLE INITIALLY DEFERRED"; assertEquals(PostgreSqlQueryGenerator.getSqlCreateForeignKey(entityMeta, refAttr), expectedSql); } + @Test + public void getSqlDropForeignKey() + { + AttributeMetaData refIdAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr").getMock(); + EntityMetaData refEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity").getMock(); + when(refEntityMeta.getIdAttribute()).thenReturn(refIdAttr); + + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData refAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(refAttr.getDataType()).thenReturn(XREF); + when(refAttr.getRefEntity()).thenReturn(refEntityMeta); + + String expectedSql = "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_fkey\""; + assertEquals(PostgreSqlQueryGenerator.getSqlDropForeignKey(entityMeta, refAttr), expectedSql); + } + + @Test + public void getSqlCreateUniqueKey() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + + String expectedSql = "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_key\" UNIQUE (\"attr\")"; + assertEquals(PostgreSqlQueryGenerator.getSqlCreateUniqueKey(entityMeta, attr), expectedSql); + } + + @Test + public void getSqlDropUniqueKey() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + + String expectedSql = "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_key\""; + assertEquals(PostgreSqlQueryGenerator.getSqlDropUniqueKey(entityMeta, attr), expectedSql); + } + @Test public void getSqlCreateJunctionTable() { From 9d737f2157653f67b933fafdc15b5dd75ba713f2 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Mon, 15 Aug 2016 09:05:48 +0200 Subject: [PATCH 03/49] Add updateAttribute for single-referencing attributes to/from non-referencing attributes Add updateAttribute unit tests Fix logging statements logging wrong entity name Fix updateAttribute for entitities without id attribute (e.g. abstract entities) --- .../PostgreSqlRepositoryCollection.java | 87 ++++++--- .../PostgreSqlRepositoryCollectionTest.java | 181 ++++++++++++++++++ 2 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 711bb37b29e..ea36bc7db28 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -29,8 +29,7 @@ import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; import static org.molgenis.data.postgresql.PostgreSqlQueryGenerator.*; import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.*; -import static org.molgenis.data.support.EntityMetaDataUtils.isMultipleReferenceType; -import static org.molgenis.data.support.EntityMetaDataUtils.isSingleReferenceType; +import static org.molgenis.data.support.EntityMetaDataUtils.*; public class PostgreSqlRepositoryCollection extends AbstractRepositoryCollection { @@ -157,7 +156,8 @@ public void deleteRepository(EntityMetaData entityMeta) String sqlDropJunctionTable = getSqlDropJunctionTable(entityMeta, mrefAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping junction table for entity [{}] attribute [{}]", POSTGRESQL, mrefAttr.getName()); + LOG.debug("Dropping junction table for entity [{}] attribute [{}]", entityMeta.getName(), + mrefAttr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", sqlDropJunctionTable); @@ -169,7 +169,7 @@ public void deleteRepository(EntityMetaData entityMeta) String sqlDropTable = getSqlDropTable(entityMeta); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping table for entity [{}]", POSTGRESQL); + LOG.debug("Dropping table for entity [{}]", entityMeta.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", sqlDropTable); @@ -198,10 +198,10 @@ public void addAttribute(String entityName, AttributeMetaData attribute) /** * Adds an attribute to the repository. * - * @param entityMetaData entity meta data that owns the attribute + * @param entityMeta entity meta data that owns the attribute * @param attr the {@link AttributeMetaData} to add */ - private void addAttributeRec(EntityMetaData entityMetaData, AttributeMetaData attr) + private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr) { // FIXME code duplication with create table if (attr.getExpression() != null) @@ -211,10 +211,11 @@ private void addAttributeRec(EntityMetaData entityMetaData, AttributeMetaData at } if (isMultipleReferenceType(attr)) { - String createJunctionTableSql = getSqlCreateJunctionTable(entityMetaData, attr); + String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating junction table for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); + LOG.debug("Creating junction table for entity [{}] attribute [{}]", entityMeta.getName(), + attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createJunctionTableSql); @@ -224,10 +225,10 @@ private void addAttributeRec(EntityMetaData entityMetaData, AttributeMetaData at } else if (attr.getDataType() != COMPOUND) { - String addColumnSql = getSqlAddColumn(entityMetaData, attr); + String addColumnSql = getSqlAddColumn(entityMeta, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating column for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); + LOG.debug("Creating column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", addColumnSql); @@ -239,10 +240,10 @@ else if (attr.getDataType() != COMPOUND) // FIXME Code duplication if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) { - String createForeignKeySql = getSqlCreateForeignKey(entityMetaData, attr); + String createForeignKeySql = getSqlCreateForeignKey(entityMeta, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating foreign key for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); + LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createForeignKeySql); @@ -251,13 +252,13 @@ else if (attr.getDataType() != COMPOUND) jdbcTemplate.execute(createForeignKeySql); } - String idAttrName = entityMetaData.getIdAttribute().getName(); + String idAttrName = entityMeta.getIdAttribute().getName(); if (attr.isUnique() && !attr.getName().equals(idAttrName)) { - String createUniqueKeySql = getSqlCreateUniqueKey(entityMetaData, attr); + String createUniqueKeySql = getSqlCreateUniqueKey(entityMeta, attr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating unique key for entity [{}] attribute [{}]", POSTGRESQL, attr.getName()); + LOG.debug("Creating unique key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", createUniqueKeySql); @@ -270,7 +271,7 @@ else if (attr.getDataType() != COMPOUND) { for (AttributeMetaData attrPart : attr.getAttributeParts()) { - addAttributeRec(entityMetaData, attrPart); + addAttributeRec(entityMeta, attrPart); } } } @@ -298,19 +299,35 @@ public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData att } } - private void updateDataType(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { - if (entityMetaData.getIdAttribute().getName().equals(attr.getName())) + AttributeMetaData idAttr = entityMeta.getIdAttribute(); + if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( format("Data type of entity [%s] attribute [%s] cannot be modified, because [%s] is an ID attribute.", - entityMetaData.getName(), attr.getName(), attr.getName())); + entityMeta.getName(), attr.getName(), attr.getName())); + } + + // remove foreign key on data type updates such as XREF --> STRING + if (isSingleReferenceType(attr) && !isReferenceType(updatedAttr)) + { + String sqlDropForeignKey = PostgreSqlQueryGenerator.getSqlDropForeignKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlDropForeignKey); + } + } + jdbcTemplate.execute(sqlDropForeignKey); } - String sqlSetDataType = getSqlSetDataType(entityMetaData, updatedAttr); + String sqlSetDataType = getSqlSetDataType(entityMeta, updatedAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Changing data type of entity [{}] attribute [{}] from [{}] to [{}]", entityMetaData.getName(), + LOG.debug("Changing data type of entity [{}] attribute [{}] from [{}] to [{}]", entityMeta.getName(), attr.getName(), attr.getDataType().toString(), updatedAttr.getDataType().toString()); if (LOG.isTraceEnabled()) { @@ -318,13 +335,30 @@ private void updateDataType(EntityMetaData entityMetaData, AttributeMetaData att } } jdbcTemplate.execute(sqlSetDataType); + + // add foreign key on data type updates such as STRING --> XREF + if (!isReferenceType(attr) && isSingleReferenceType(updatedAttr)) + { + String sqlCreateForeignKey = getSqlCreateForeignKey(entityMeta, updatedAttr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), + updatedAttr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlCreateForeignKey); + } + } + jdbcTemplate.execute(sqlCreateForeignKey); + } } private void updateUnique(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) { if (attr.isUnique() && !updatedAttr.isUnique()) { - if (entityMetaData.getIdAttribute().getName().equals(attr.getName())) + AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), @@ -370,12 +404,12 @@ public void deleteAttribute(String entityName, String attributeName) deleteAttribute(entityMetaData, attributeName); } - private void deleteAttribute(EntityMetaData entityMetaData, String attrName) + private void deleteAttribute(EntityMetaData entityMeta, String attrName) { - String dropColumnSql = getSqlDropColumn(entityMetaData, attrName); + String dropColumnSql = getSqlDropColumn(entityMeta, attrName); if (LOG.isDebugEnabled()) { - LOG.debug("Dropping column for entity [{}] attribute [{}]", POSTGRESQL, attrName); + LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attrName); if (LOG.isTraceEnabled()) { LOG.trace("SQL: {}", dropColumnSql); @@ -447,7 +481,8 @@ private void updateNillable(EntityMetaData entityMetaData, AttributeMetaData att } else if (!attr.isNillable() && updatedAttr.isNillable()) { - if (entityMetaData.getIdAttribute().getName().equals(attr.getName())) + AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( format("ID attribute [%s] of entity [%s] cannot be nullable", attr.getName(), diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java new file mode 100644 index 00000000000..5da39cbd6e4 --- /dev/null +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -0,0 +1,181 @@ +package org.molgenis.data.postgresql; + +import org.mockito.ArgumentCaptor; +import org.molgenis.data.DataService; +import org.molgenis.data.MolgenisDataException; +import org.molgenis.data.meta.model.AttributeMetaData; +import org.molgenis.data.meta.model.EntityMetaData; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.sql.DataSource; + +import static com.google.common.collect.Lists.newArrayList; +import static org.mockito.ArgumentCaptor.forClass; +import static org.mockito.Mockito.*; +import static org.molgenis.MolgenisFieldTypes.AttributeType.*; +import static org.testng.Assert.assertEquals; + +public class PostgreSqlRepositoryCollectionTest +{ + private PostgreSqlRepositoryCollection postgreSqlRepoCollection; + private JdbcTemplate jdbcTemplate; + + @BeforeMethod + public void setUpBeforeMethod() + { + PostgreSqlEntityFactory postgreSqlEntityFactory = mock(PostgreSqlEntityFactory.class); + DataSource dataSource = mock(DataSource.class); + jdbcTemplate = mock(JdbcTemplate.class); + DataService dataService = mock(DataService.class); + PlatformTransactionManager platformTransactionManager = mock(PlatformTransactionManager.class); + postgreSqlRepoCollection = new PostgreSqlRepositoryCollection(postgreSqlEntityFactory, dataSource, jdbcTemplate, + dataService, platformTransactionManager); + } + + @Test + public void updateAttributeNillableToNotNillable() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isNillable()).thenReturn(false); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate).execute(captor.capture()); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET NOT NULL"); + } + + @Test + public void updateAttributeNotNillableToNillable() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate).execute(captor.capture()); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" DROP NOT NULL"); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void updateAttributeNotNillableToNillableIdAttr() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(attr); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + } + + @Test + public void updateAttributeUniqueToNotUnique() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isUnique()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isUnique()).thenReturn(false); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate).execute(captor.capture()); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attrNew_key\""); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void updateAttributeUniqueToNotUniqueIdAttr() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(attr); + when(attr.isUnique()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isUnique()).thenReturn(false); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + } + + @Test + public void updateAttributeNotUniqueToUnique() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isUnique()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isUnique()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate).execute(captor.capture()); + assertEquals(captor.getValue(), + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attrNew_key\" UNIQUE (\"attrNew\")"); + } + + @Test + public void updateAttributeDataTypeToDataType() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.getDataType()).thenReturn(INT); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate).execute(captor.capture()); + assertEquals(captor.getValue(), + "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE integer USING \"attrNew\"::integer"); + } + + @Test + public void updateAttributeSingleRefDataTypeToDataType() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(XREF); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.getDataType()).thenReturn(STRING); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(2)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_fkey\"", + "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE character varying(255) USING \"attrNew\"::character varying(255)")); + } + + @Test + public void updateAttributeDataTypeToSingleRefDataType() throws Exception + { + AttributeMetaData refIdAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr").getMock(); + when(refIdAttr.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity").getMock(); + when(refEntityMeta.getIdAttribute()).thenReturn(refIdAttr); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.getDataType()).thenReturn(XREF); + when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(2)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList( + "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE character varying(255) USING \"attrNew\"::character varying(255)", + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attrNew_fkey\" FOREIGN KEY (\"attrNew\") REFERENCES \"refEntity\"(\"refIdAttr\")")); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void updateAttributeDataTypeToDataTypeIdAttr() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(attr); + when(attr.getDataType()).thenReturn(STRING); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.getDataType()).thenReturn(INT); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + } +} \ No newline at end of file From da31fa5bd4860a6818bf97e186fe35c2e225b520 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Mon, 15 Aug 2016 09:13:12 +0200 Subject: [PATCH 04/49] Add allowed data type transitions for single-reference attribute types --- .../AttributeMetaDataRepositoryDecorator.java | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java index 98823eaf960..56a797522b8 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java @@ -364,37 +364,44 @@ private void validateUpdate(AttributeMetaData currentAttr, AttributeMetaData new static { DATA_TYPE_ALLOWED_TRANSITIONS = new EnumMap<>(AttributeType.class); - DATA_TYPE_ALLOWED_TRANSITIONS.put(BOOL, EnumSet.of(DECIMAL, INT, LONG, SCRIPT, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL, EnumSet.of(XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS + .put(BOOL, EnumSet.of(CATEGORICAL, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); + // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF + // not allowed : CATEGORICAL, COMPOUND, FILE + DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL, + EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL_MREF, EnumSet.of(MREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.noneOf(AttributeType.class)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE, EnumSet.of(DATE_TIME, HTML, SCRIPT, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(DATE, HTML, SCRIPT, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DECIMAL, EnumSet.of(HTML, INT, LONG, SCRIPT, TEXT)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL, DATE_TIME, HTML, SCRIPT, STRING, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL, DATE, HTML, SCRIPT, STRING, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(DECIMAL, EnumSet.of(CATEGORICAL, HTML, INT, LONG, SCRIPT, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); DATA_TYPE_ALLOWED_TRANSITIONS.put(ENUM, EnumSet.noneOf(AttributeType.class)); DATA_TYPE_ALLOWED_TRANSITIONS.put(FILE, EnumSet.noneOf(AttributeType.class)); // not implemented: HTML -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF // not allowed : HTML -> COMPOUND, FILE, HTML - DATA_TYPE_ALLOWED_TRANSITIONS - .put(HTML, EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(HTML, + EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(INT, EnumSet.of(DECIMAL, HTML, LONG, SCRIPT, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(LONG, EnumSet.of(DECIMAL, HTML, INT, SCRIPT, TEXT)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(INT, EnumSet.of(CATEGORICAL, DECIMAL, HTML, LONG, SCRIPT, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(LONG, EnumSet.of(CATEGORICAL, DECIMAL, HTML, INT, SCRIPT, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(MREF, EnumSet.of(CATEGORICAL_MREF)); // not implemented: SCRIPT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF // not allowed : SCRIPT -> COMPOUND, FILE, SCRIPT - DATA_TYPE_ALLOWED_TRANSITIONS - .put(SCRIPT, EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, STRING, TEXT)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(SCRIPT, + EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, STRING, TEXT, XREF)); // not implemented: TEXT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF // not allowed : TEXT -> COMPOUND, FILE, TEXT // not implemented: STRING -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF // not allowed : STRING -> COMPOUND, FILE, STRING - DATA_TYPE_ALLOWED_TRANSITIONS - .put(STRING, EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(TEXT, EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(XREF, EnumSet.of(CATEGORICAL)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(STRING, + EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS.put(TEXT, + EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, XREF)); + // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF + // not allowed : COMPOUND, FILE, XREF + DATA_TYPE_ALLOWED_TRANSITIONS.put(XREF, + EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT)); } private static void validateUpdateDataType(AttributeType currentDataType, AttributeType newDataType) @@ -404,7 +411,8 @@ private static void validateUpdateDataType(AttributeType currentDataType, Attrib { throw new MolgenisDataException( format("Attribute data type update from [%s] to [%s] not allowed, allowed types are %s", - currentDataType.toString(), newDataType.toString(), allowedDataTypes.toString())); + currentDataType.toString(), newDataType.toString(), + allowedDataTypes != null ? allowedDataTypes.toString() : "[]")); } } From ecfa665cf2ef6fa8ae3b0c10d9dbc90e5435d5a3 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Mon, 15 Aug 2016 10:00:52 +0200 Subject: [PATCH 05/49] Fix attribute update of abstract entity --- .../PostgreSqlRepositoryCollection.java | 36 ++++++++------ .../PostgreSqlRepositoryCollectionTest.java | 47 ++++++++++++++++++- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index ea36bc7db28..501ddf3601c 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -199,7 +199,7 @@ public void addAttribute(String entityName, AttributeMetaData attribute) * Adds an attribute to the repository. * * @param entityMeta entity meta data that owns the attribute - * @param attr the {@link AttributeMetaData} to add + * @param attr the {@link AttributeMetaData} to add */ private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr) { @@ -279,23 +279,31 @@ else if (attr.getDataType() != COMPOUND) @Override public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) { - // FIXME updating attributes in abstract entities - // nullable changes - if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + if (entityMetaData.isAbstract()) { - updateNillable(entityMetaData, attr, updatedAttr); + // for abstract entities recursively update entities extending the abstract entity + dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(EXTENDS, entityMetaData).findAll() + .forEach(childEntityMeta -> updateAttribute(childEntityMeta, attr, updatedAttr)); } - - // unique changes - if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + else { - updateUnique(entityMetaData, attr, updatedAttr); - } + // nullable changes + if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + { + updateNillable(entityMetaData, attr, updatedAttr); + } - // data type changes - if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) - { - updateDataType(entityMetaData, attr, updatedAttr); + // unique changes + if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + { + updateUnique(entityMetaData, attr, updatedAttr); + } + + // data type changes + if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) + { + updateDataType(entityMetaData, attr, updatedAttr); + } } } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 5da39cbd6e4..6800c29df58 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -3,6 +3,7 @@ import org.mockito.ArgumentCaptor; import org.molgenis.data.DataService; import org.molgenis.data.MolgenisDataException; +import org.molgenis.data.Query; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.springframework.jdbc.core.JdbcTemplate; @@ -11,17 +12,21 @@ import org.testng.annotations.Test; import javax.sql.DataSource; +import java.util.stream.Stream; import static com.google.common.collect.Lists.newArrayList; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Mockito.*; import static org.molgenis.MolgenisFieldTypes.AttributeType.*; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ENTITY_META_DATA; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.EXTENDS; import static org.testng.Assert.assertEquals; public class PostgreSqlRepositoryCollectionTest { private PostgreSqlRepositoryCollection postgreSqlRepoCollection; private JdbcTemplate jdbcTemplate; + private DataService dataService; @BeforeMethod public void setUpBeforeMethod() @@ -29,7 +34,7 @@ public void setUpBeforeMethod() PostgreSqlEntityFactory postgreSqlEntityFactory = mock(PostgreSqlEntityFactory.class); DataSource dataSource = mock(DataSource.class); jdbcTemplate = mock(JdbcTemplate.class); - DataService dataService = mock(DataService.class); + dataService = mock(DataService.class); PlatformTransactionManager platformTransactionManager = mock(PlatformTransactionManager.class); postgreSqlRepoCollection = new PostgreSqlRepositoryCollection(postgreSqlEntityFactory, dataSource, jdbcTemplate, dataService, platformTransactionManager); @@ -178,4 +183,44 @@ public void updateAttributeDataTypeToDataTypeIdAttr() throws Exception when(updatedAttr.getDataType()).thenReturn(INT); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); } + + @Test + public void updateAttributeAbstractEntity() + { + EntityMetaData abstractEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("root").getMock(); + when(abstractEntityMeta.isAbstract()).thenReturn(true); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + when(updatedAttr.isNillable()).thenReturn(false); + + EntityMetaData entityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("entity0").getMock(); + when(entityMeta0.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta0.isAbstract()).thenReturn(true); + EntityMetaData entityMeta0a = when(mock(EntityMetaData.class).getName()).thenReturn("entity0a").getMock(); + when(entityMeta0a.getExtends()).thenReturn(entityMeta0); + when(entityMeta0a.isAbstract()).thenReturn(false); + EntityMetaData entityMeta0b = when(mock(EntityMetaData.class).getName()).thenReturn("entity0b").getMock(); + when(entityMeta0b.getExtends()).thenReturn(entityMeta0); + when(entityMeta0b.isAbstract()).thenReturn(false); + EntityMetaData entityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("entity1").getMock(); + when(entityMeta1.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta1.isAbstract()).thenReturn(false); + + Query entityQ = mock(Query.class); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + Query entityQ0 = mock(Query.class); + Query entityQ1 = mock(Query.class); + when(entityQ.eq(EXTENDS, abstractEntityMeta)).thenReturn(entityQ0); + when(entityQ.eq(EXTENDS, entityMeta0)).thenReturn(entityQ1); + when(entityQ0.findAll()).thenReturn(Stream.of(entityMeta0, entityMeta1)); + when(entityQ1.findAll()).thenReturn(Stream.of(entityMeta0a, entityMeta0b)); + postgreSqlRepoCollection.updateAttribute(abstractEntityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(3)).execute(captor.capture()); + assertEquals(captor.getAllValues(), + newArrayList("ALTER TABLE \"entity0a\" ALTER COLUMN \"attrNew\" SET NOT NULL", + "ALTER TABLE \"entity0b\" ALTER COLUMN \"attrNew\" SET NOT NULL", + "ALTER TABLE \"entity1\" ALTER COLUMN \"attrNew\" SET NOT NULL")); + } } \ No newline at end of file From a09673f84ca681f3380b82695a10833cae9ffaeb Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Mon, 15 Aug 2016 14:10:10 +0200 Subject: [PATCH 06/49] Handle to/from compound attribute type changes Handle to/from attribute expression changes Handle compound attributes consistently in PostgreSQL repository collection --- .../postgresql/PostgreSqlQueryGenerator.java | 4 +- .../PostgreSqlRepositoryCollection.java | 323 ++++++++------- .../PostgreSqlQueryGeneratorTest.java | 9 + .../PostgreSqlRepositoryCollectionTest.java | 378 ++++++++++++++++-- .../molgenis/data/RepositoryCollection.java | 22 +- .../data/RepositoryCollectionDecorator.java | 8 +- .../EntityMetaDataRepositoryDecorator.java | 10 +- ...exActionRepositoryCollectionDecorator.java | 12 +- .../support/AbstractRepositoryCollection.java | 4 +- ...tionRepositoryCollectionDecoratorTest.java | 11 +- 10 files changed, 568 insertions(+), 213 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index fbcff7db654..a33ba257b83 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -195,10 +195,10 @@ static String getSqlDropTable(EntityMetaData entityMeta) return getSqlDropTable(getTableName(entityMeta)); } - static String getSqlDropColumn(EntityMetaData entityMeta, String attrName) + static String getSqlDropColumn(EntityMetaData entityMeta, AttributeMetaData attr) { return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP COLUMN ") - .append(getColumnName(attrName)).toString(); + .append(getColumnName(attr)).toString(); } static String getSqlInsert(EntityMetaData entityMeta) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 501ddf3601c..0d2f88e85d5 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -43,7 +43,7 @@ public class PostgreSqlRepositoryCollection extends AbstractRepositoryCollection private final DataService dataService; private final PlatformTransactionManager transactionManager; - public PostgreSqlRepositoryCollection(PostgreSqlEntityFactory postgreSqlEntityFactory, DataSource dataSource, + PostgreSqlRepositoryCollection(PostgreSqlEntityFactory postgreSqlEntityFactory, DataSource dataSource, JdbcTemplate jdbcTemplate, DataService dataService, PlatformTransactionManager transactionManager) { this.postgreSqlEntityFactory = requireNonNull(postgreSqlEntityFactory); @@ -76,8 +76,7 @@ public Stream getLanguageCodes() { if (isTableExists(LANGUAGE)) { - String sql = new StringBuilder("SELECT \"").append(CODE).append("\" FROM \"").append(LANGUAGE).append('"') - .toString(); + String sql = "SELECT \"" + CODE + "\" FROM \"" + LANGUAGE + '"'; if (LOG.isDebugEnabled()) { LOG.debug("Fetching languages"); @@ -179,130 +178,126 @@ public void deleteRepository(EntityMetaData entityMeta) } @Override - public void addAttribute(String entityName, AttributeMetaData attribute) + public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attr) { - EntityMetaData entityMetaData = dataService.getEntityMetaData(entityName); - if (entityMetaData == null) - { - throw new UnknownEntityException( - format("Adding attribute operation failed. Unknown entity [%s]", entityName)); - } - if (null != entityMetaData.getAttribute(requireNonNull(attribute).getName())) - { - throw new MolgenisDataException( - format("Adding attribute operation failed. Attribute already exists [%s]", attribute.getName())); - } - addAttributeRec(entityMetaData, attribute); + addAttributeRec(entityMeta, attr, true); } /** - * Adds an attribute to the repository. + * Recursively add attribute to entity and entities extending from this entity. * - * @param entityMeta entity meta data that owns the attribute - * @param attr the {@link AttributeMetaData} to add + * @param entityMeta entity meta data + * @param attr attribute to add + * @param checkAttrExists whether or not to perform a check if the attribute exists for the given entity */ - private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr) + private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr, boolean checkAttrExists) { - // FIXME code duplication with create table - if (attr.getExpression() != null) + if (attr.getExpression() != null || attr.getDataType() == COMPOUND) { - // computed attributes are not persisted + // computed attributes and compound attributes are not persisted return; } - if (isMultipleReferenceType(attr)) + + if (checkAttrExists && entityMeta.getAttribute(attr.getName()) != null) { - String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating junction table for entity [{}] attribute [{}]", entityMeta.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createJunctionTableSql); - } - } - jdbcTemplate.execute(createJunctionTableSql); + throw new MolgenisDataException( + format("Adding attribute operation failed. Attribute already exists [%s]", attr.getName())); } - else if (attr.getDataType() != COMPOUND) + + if (entityMeta.isAbstract()) { - String addColumnSql = getSqlAddColumn(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", addColumnSql); - } - } - jdbcTemplate.execute(addColumnSql); + // for abstract entities recursively update entities extending the abstract entity + dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(EXTENDS, entityMeta).findAll() + .forEach(childEntityMeta -> addAttributeRec(childEntityMeta, attr, checkAttrExists)); } - - // FIXME Code duplication - if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) + else { - String createForeignKeySql = getSqlCreateForeignKey(entityMeta, attr); - if (LOG.isDebugEnabled()) + if (isMultipleReferenceType(attr)) { - LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createForeignKeySql); - } + createJunctionTable(entityMeta, attr); + } + else if (attr.getDataType() != COMPOUND) + { + createColumn(entityMeta, attr); } - jdbcTemplate.execute(createForeignKeySql); - } - String idAttrName = entityMeta.getIdAttribute().getName(); - if (attr.isUnique() && !attr.getName().equals(idAttrName)) - { - String createUniqueKeySql = getSqlCreateUniqueKey(entityMeta, attr); - if (LOG.isDebugEnabled()) + if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) { - LOG.debug("Creating unique key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createUniqueKeySql); - } + createForeignKey(entityMeta, attr); } - jdbcTemplate.execute(createUniqueKeySql); - } - if (attr.getDataType() == COMPOUND) - { - for (AttributeMetaData attrPart : attr.getAttributeParts()) + String idAttrName = entityMeta.getIdAttribute().getName(); + if (attr.isUnique() && !attr.getName().equals(idAttrName)) { - addAttributeRec(entityMeta, attrPart); + createUniqueKey(entityMeta, attr); } } } @Override - public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + public void updateAttribute(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (entityMeta.getAttribute(attr.getName()) == null) + { + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + } + updateAttributeRec(entityMeta, attr, updatedAttr); + } + + /** + * Recursively update attribute of entity and entities extending from this entity. + * + * @param entityMeta entity meta data + * @param attr existing attribute + * @param updatedAttr updated attribute + */ + private void updateAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { - if (entityMetaData.isAbstract()) + if ((attr.getExpression() != null && updatedAttr.getExpression() != null) || (attr.getDataType() == COMPOUND + && updatedAttr.getDataType() == COMPOUND)) + { + return; + } + + if (entityMeta.isAbstract()) { // for abstract entities recursively update entities extending the abstract entity - dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(EXTENDS, entityMetaData).findAll() - .forEach(childEntityMeta -> updateAttribute(childEntityMeta, attr, updatedAttr)); + dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(EXTENDS, entityMeta).findAll() + .forEach(childEntityMeta -> updateAttributeRec(childEntityMeta, attr, updatedAttr)); } else { - // nullable changes - if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + if ((attr.getExpression() == null && updatedAttr.getExpression() != null) || (attr.getDataType() != COMPOUND + && updatedAttr.getDataType() == COMPOUND)) { - updateNillable(entityMetaData, attr, updatedAttr); + // computed attributes and compound attributes are not persisted + deleteAttribute(entityMeta, attr); } - - // unique changes - if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + else if ((attr.getExpression() != null && updatedAttr.getExpression() == null) || ( + attr.getDataType() == COMPOUND && updatedAttr.getDataType() != COMPOUND)) { - updateUnique(entityMetaData, attr, updatedAttr); + // computed attributes and compound attributes are not persisted + addAttributeRec(entityMeta, updatedAttr, false); } - - // data type changes - if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) + else { - updateDataType(entityMetaData, attr, updatedAttr); + // nullable changes + if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + { + updateNillable(entityMeta, attr, updatedAttr); + } + + // unique changes + if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + { + updateUnique(entityMeta, attr, updatedAttr); + } + + // data type changes + if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) + { + updateDataType(entityMeta, attr, updatedAttr); + } } } } @@ -347,17 +342,7 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A // add foreign key on data type updates such as STRING --> XREF if (!isReferenceType(attr) && isSingleReferenceType(updatedAttr)) { - String sqlCreateForeignKey = getSqlCreateForeignKey(entityMeta, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), - updatedAttr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlCreateForeignKey); - } - } - jdbcTemplate.execute(sqlCreateForeignKey); + createForeignKey(entityMeta, updatedAttr); } } @@ -402,28 +387,48 @@ else if (!attr.isUnique() && updatedAttr.isUnique()) } @Override - public void deleteAttribute(String entityName, String attributeName) + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) { - EntityMetaData entityMetaData = dataService.getEntityMetaData(entityName); - if (entityMetaData == null) + if (entityMeta.getAttribute(attr.getName()) == null) { - throw new UnknownEntityException(format("Unknown entity [%s]", entityName)); + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); } - deleteAttribute(entityMetaData, attributeName); + deleteAttributeRec(entityMeta, attr); } - private void deleteAttribute(EntityMetaData entityMeta, String attrName) + /** + * Recursively delete attribute in entity and entities extending from this entity. + * + * @param entityMeta entity meta data + * @param attr attribute to delete + */ + private void deleteAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr) { - String dropColumnSql = getSqlDropColumn(entityMeta, attrName); - if (LOG.isDebugEnabled()) + if (attr.getExpression() != null || attr.getDataType() == COMPOUND) { - LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attrName); - if (LOG.isTraceEnabled()) + // computed attributes and compound attributes are not persisted + return; + } + + if (entityMeta.isAbstract()) + { + // for abstract entities recursively update entities extending the abstract entity + dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(EXTENDS, entityMeta).findAll() + .forEach(childEntityMeta -> deleteAttributeRec(childEntityMeta, attr)); + } + else + { + String dropColumnSql = getSqlDropColumn(entityMeta, attr); + if (LOG.isDebugEnabled()) { - LOG.trace("SQL: {}", dropColumnSql); + LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropColumnSql); + } } + jdbcTemplate.execute(dropColumnSql); } - jdbcTemplate.execute(dropColumnSql); } /** @@ -466,6 +471,7 @@ private boolean isTableExists(String tableName) } catch (Exception e) { + //noinspection ThrowFromFinallyBlock throw new RuntimeException(e); } } @@ -531,48 +537,73 @@ private void create(EntityMetaData entityMeta) // add mref tables if (isMultipleReferenceType(attr)) { - String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating junction table for entity [{}] attribute [{}]", entityMeta.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createJunctionTableSql); - } - } - jdbcTemplate.execute(createJunctionTableSql); + createJunctionTable(entityMeta, attr); } - // FIXME Code duplication else if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) { - String createForeignKeySql = getSqlCreateForeignKey(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createForeignKeySql); - } - } - jdbcTemplate.execute(createForeignKeySql); + createForeignKey(entityMeta, attr); } if (attr.isUnique() && !attr.getName().equals(idAttrName)) { - String createUniqueSql = getSqlCreateUniqueKey(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating unique key for entity [{}] attribute [{}]", entityMeta.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", createUniqueSql); - } - } - jdbcTemplate.execute(createUniqueSql); + createUniqueKey(entityMeta, attr); } }); } + + private void createForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String createForeignKeySql = getSqlCreateForeignKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", createForeignKeySql); + } + } + jdbcTemplate.execute(createForeignKeySql); + } + + private void createUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String createUniqueKeySql = getSqlCreateUniqueKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating unique key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", createUniqueKeySql); + } + } + jdbcTemplate.execute(createUniqueKeySql); + } + + private void createColumn(EntityMetaData entityMeta, AttributeMetaData attr) + { + String addColumnSql = getSqlAddColumn(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", addColumnSql); + } + } + jdbcTemplate.execute(addColumnSql); + } + + private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) + { + String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating junction table for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", createJunctionTableSql); + } + } + jdbcTemplate.execute(createJunctionTableSql); + } } \ No newline at end of file diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index 6a62b697740..da292c47261 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -268,4 +268,13 @@ public void getSqlAddColumnInvalidType(AttributeType attrType) when(attr.getDataType()).thenReturn(attrType); PostgreSqlQueryGenerator.getSqlAddColumn(entityMeta, attr); } + + @Test + public void getSqlDropColumn() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + assertEquals(PostgreSqlQueryGenerator.getSqlDropColumn(entityMeta, attr), + "ALTER TABLE \"entity\" DROP COLUMN \"attr\""); + } } \ No newline at end of file diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 6800c29df58..d6c80948f6a 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -4,6 +4,7 @@ import org.molgenis.data.DataService; import org.molgenis.data.MolgenisDataException; import org.molgenis.data.Query; +import org.molgenis.data.UnknownAttributeException; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.springframework.jdbc.core.JdbcTemplate; @@ -40,42 +41,62 @@ public void setUpBeforeMethod() dataService, platformTransactionManager); } + @Test + public void updateAttribute() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getLabel()).thenReturn("label"); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getLabel()).thenReturn("updated label"); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verifyZeroInteractions(jdbcTemplate); + } + @Test public void updateAttributeNillableToNotNillable() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isNillable()).thenReturn(true); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isNillable()).thenReturn(false); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate).execute(captor.capture()); - assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET NOT NULL"); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET NOT NULL"); } @Test public void updateAttributeNotNillableToNillable() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isNillable()).thenReturn(false); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isNillable()).thenReturn(true); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate).execute(captor.capture()); - assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" DROP NOT NULL"); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" DROP NOT NULL"); } @Test(expectedExceptions = MolgenisDataException.class) public void updateAttributeNotNillableToNillableIdAttr() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(entityMeta.getIdAttribute()).thenReturn(attr); when(attr.isNillable()).thenReturn(false); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isNillable()).thenReturn(true); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); } @@ -84,24 +105,28 @@ public void updateAttributeNotNillableToNillableIdAttr() throws Exception public void updateAttributeUniqueToNotUnique() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isUnique()).thenReturn(true); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isUnique()).thenReturn(false); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate).execute(captor.capture()); - assertEquals(captor.getValue(), "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attrNew_key\""); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_key\""); } @Test(expectedExceptions = MolgenisDataException.class) public void updateAttributeUniqueToNotUniqueIdAttr() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); when(entityMeta.getIdAttribute()).thenReturn(attr); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isUnique()).thenReturn(true); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isUnique()).thenReturn(false); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); } @@ -110,45 +135,50 @@ public void updateAttributeUniqueToNotUniqueIdAttr() throws Exception public void updateAttributeNotUniqueToUnique() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isUnique()).thenReturn(false); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isUnique()).thenReturn(true); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate).execute(captor.capture()); - assertEquals(captor.getValue(), - "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attrNew_key\" UNIQUE (\"attrNew\")"); + assertEquals(captor.getValue(), "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_key\" UNIQUE (\"attr\")"); } @Test public void updateAttributeDataTypeToDataType() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.getDataType()).thenReturn(STRING); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.getDataType()).thenReturn(INT); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate).execute(captor.capture()); assertEquals(captor.getValue(), - "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE integer USING \"attrNew\"::integer"); + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET DATA TYPE integer USING \"attr\"::integer"); } @Test public void updateAttributeSingleRefDataTypeToDataType() throws Exception { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.getDataType()).thenReturn(XREF); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.getDataType()).thenReturn(STRING); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate, times(2)).execute(captor.capture()); assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_fkey\"", - "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE character varying(255) USING \"attrNew\"::character varying(255)")); + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET DATA TYPE character varying(255) USING \"attr\"::character varying(255)")); } @Test @@ -159,17 +189,19 @@ public void updateAttributeDataTypeToSingleRefDataType() throws Exception EntityMetaData refEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity").getMock(); when(refEntityMeta.getIdAttribute()).thenReturn(refIdAttr); EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.getDataType()).thenReturn(STRING); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.getDataType()).thenReturn(XREF); when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate, times(2)).execute(captor.capture()); assertEquals(captor.getAllValues(), newArrayList( - "ALTER TABLE \"entity\" ALTER COLUMN \"attrNew\" SET DATA TYPE character varying(255) USING \"attrNew\"::character varying(255)", - "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attrNew_fkey\" FOREIGN KEY (\"attrNew\") REFERENCES \"refEntity\"(\"refIdAttr\")")); + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET DATA TYPE character varying(255) USING \"attr\"::character varying(255)", + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity\"(\"refIdAttr\")")); } @Test(expectedExceptions = MolgenisDataException.class) @@ -179,19 +211,119 @@ public void updateAttributeDataTypeToDataTypeIdAttr() throws Exception AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); when(entityMeta.getIdAttribute()).thenReturn(attr); when(attr.getDataType()).thenReturn(STRING); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); when(updatedAttr.getDataType()).thenReturn(INT); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); } + @Test + public void updateAttributeWithExpressionBefore() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getExpression()).thenReturn("expression"); + when(attr.getDataType()).thenReturn(STRING); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getExpression()).thenReturn(null); + when(updatedAttr.getDataType()).thenReturn(STRING); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" ADD \"attr\" character varying(255)"); + } + + @Test + public void updateAttributeWithExpressionAfter() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getExpression()).thenReturn(null); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getExpression()).thenReturn("expression"); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" DROP COLUMN \"attr\""); + } + + @Test + public void updateAttributeWithExpressionBeforeAfter() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getExpression()).thenReturn("expression"); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getExpression()).thenReturn("expression"); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verifyZeroInteractions(jdbcTemplate); + } + + @Test + public void updateAttributeCompoundBefore() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(COMPOUND); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(STRING); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL"); + } + + @Test + public void updateAttributeCompoundAfter() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(STRING); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(COMPOUND); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" DROP COLUMN \"attr\""); + } + + @Test + public void updateAttributeCompoundBeforeAfter() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(COMPOUND); + when(attr.isNillable()).thenReturn(false); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(COMPOUND); + when(updatedAttr.isNillable()).thenReturn(true); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verifyZeroInteractions(jdbcTemplate); + } + @Test public void updateAttributeAbstractEntity() { EntityMetaData abstractEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("root").getMock(); when(abstractEntityMeta.isAbstract()).thenReturn(true); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(abstractEntityMeta.getAttribute(attrName)).thenReturn(attr); when(attr.isNillable()).thenReturn(true); - AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attrNew").getMock(); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); when(updatedAttr.isNillable()).thenReturn(false); EntityMetaData entityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("entity0").getMock(); @@ -207,20 +339,202 @@ public void updateAttributeAbstractEntity() when(entityMeta1.getExtends()).thenReturn(abstractEntityMeta); when(entityMeta1.isAbstract()).thenReturn(false); + //noinspection unchecked Query entityQ = mock(Query.class); when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + //noinspection unchecked Query entityQ0 = mock(Query.class); + //noinspection unchecked Query entityQ1 = mock(Query.class); when(entityQ.eq(EXTENDS, abstractEntityMeta)).thenReturn(entityQ0); when(entityQ.eq(EXTENDS, entityMeta0)).thenReturn(entityQ1); when(entityQ0.findAll()).thenReturn(Stream.of(entityMeta0, entityMeta1)); when(entityQ1.findAll()).thenReturn(Stream.of(entityMeta0a, entityMeta0b)); + postgreSqlRepoCollection.updateAttribute(abstractEntityMeta, attr, updatedAttr); ArgumentCaptor captor = forClass(String.class); verify(jdbcTemplate, times(3)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity0a\" ALTER COLUMN \"attr\" SET NOT NULL", + "ALTER TABLE \"entity0b\" ALTER COLUMN \"attr\" SET NOT NULL", + "ALTER TABLE \"entity1\" ALTER COLUMN \"attr\" SET NOT NULL")); + } + + @Test(expectedExceptions = UnknownAttributeException.class) + public void updateAttributeDoesNotExist() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(updatedAttr.isNillable()).thenReturn(false); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + } + + @Test + public void addAttribute() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL"); + } + + @Test + public void addAttributeAbstractEntity() + { + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + EntityMetaData abstractEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("root").getMock(); + when(abstractEntityMeta.isAbstract()).thenReturn(true); + EntityMetaData entityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("entity0").getMock(); + when(entityMeta0.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta0.isAbstract()).thenReturn(true); + EntityMetaData entityMeta0a = when(mock(EntityMetaData.class).getName()).thenReturn("entity0a").getMock(); + when(entityMeta0a.getExtends()).thenReturn(entityMeta0); + when(entityMeta0a.isAbstract()).thenReturn(false); + when(entityMeta0a.getIdAttribute()).thenReturn(idAttr); + EntityMetaData entityMeta0b = when(mock(EntityMetaData.class).getName()).thenReturn("entity0b").getMock(); + when(entityMeta0b.getExtends()).thenReturn(entityMeta0); + when(entityMeta0b.isAbstract()).thenReturn(false); + when(entityMeta0b.getIdAttribute()).thenReturn(idAttr); + EntityMetaData entityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("entity1").getMock(); + when(entityMeta1.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta1.isAbstract()).thenReturn(false); + when(entityMeta1.getIdAttribute()).thenReturn(idAttr); + + //noinspection unchecked + Query entityQ = mock(Query.class); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + //noinspection unchecked + Query entityQ0 = mock(Query.class); + //noinspection unchecked + Query entityQ1 = mock(Query.class); + when(entityQ.eq(EXTENDS, abstractEntityMeta)).thenReturn(entityQ0); + when(entityQ.eq(EXTENDS, entityMeta0)).thenReturn(entityQ1); + when(entityQ0.findAll()).thenReturn(Stream.of(entityMeta0, entityMeta1)); + when(entityQ1.findAll()).thenReturn(Stream.of(entityMeta0a, entityMeta0b)); + + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + + postgreSqlRepoCollection.addAttribute(abstractEntityMeta, attr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(3)).execute(captor.capture()); assertEquals(captor.getAllValues(), - newArrayList("ALTER TABLE \"entity0a\" ALTER COLUMN \"attrNew\" SET NOT NULL", - "ALTER TABLE \"entity0b\" ALTER COLUMN \"attrNew\" SET NOT NULL", - "ALTER TABLE \"entity1\" ALTER COLUMN \"attrNew\" SET NOT NULL")); + newArrayList("ALTER TABLE \"entity0a\" ADD \"attr\" character varying(255) NOT NULL", + "ALTER TABLE \"entity0b\" ADD \"attr\" character varying(255) NOT NULL", + "ALTER TABLE \"entity1\" ADD \"attr\" character varying(255) NOT NULL")); + } + + @Test + public void addAttributeCompound() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(COMPOUND); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + verifyZeroInteractions(jdbcTemplate); + } + + @Test + public void addAttributeWithExpression() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getExpression()).thenReturn("expression"); + when(attr.getDataType()).thenReturn(STRING); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + verifyZeroInteractions(jdbcTemplate); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void addAttributeAlreadyExists() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + } + + @Test + public void deleteAttribute() + { + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + postgreSqlRepoCollection.deleteAttribute(entityMeta, attr); + verify(jdbcTemplate).execute("ALTER TABLE \"entity\" DROP COLUMN \"attr\""); + } + + @Test + public void deleteAttributeAbstractEntity() + { + EntityMetaData abstractEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("root").getMock(); + when(abstractEntityMeta.isAbstract()).thenReturn(true); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(abstractEntityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.isNillable()).thenReturn(true); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.isNillable()).thenReturn(false); + EntityMetaData entityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("entity0").getMock(); + when(entityMeta0.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta0.isAbstract()).thenReturn(true); + EntityMetaData entityMeta0a = when(mock(EntityMetaData.class).getName()).thenReturn("entity0a").getMock(); + when(entityMeta0a.getExtends()).thenReturn(entityMeta0); + when(entityMeta0a.isAbstract()).thenReturn(false); + EntityMetaData entityMeta0b = when(mock(EntityMetaData.class).getName()).thenReturn("entity0b").getMock(); + when(entityMeta0b.getExtends()).thenReturn(entityMeta0); + when(entityMeta0b.isAbstract()).thenReturn(false); + EntityMetaData entityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("entity1").getMock(); + when(entityMeta1.getExtends()).thenReturn(abstractEntityMeta); + when(entityMeta1.isAbstract()).thenReturn(false); + + //noinspection unchecked + Query entityQ = mock(Query.class); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + //noinspection unchecked + Query entityQ0 = mock(Query.class); + //noinspection unchecked + Query entityQ1 = mock(Query.class); + when(entityQ.eq(EXTENDS, abstractEntityMeta)).thenReturn(entityQ0); + when(entityQ.eq(EXTENDS, entityMeta0)).thenReturn(entityQ1); + when(entityQ0.findAll()).thenReturn(Stream.of(entityMeta0, entityMeta1)); + when(entityQ1.findAll()).thenReturn(Stream.of(entityMeta0a, entityMeta0b)); + + postgreSqlRepoCollection.deleteAttribute(abstractEntityMeta, attr); + + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(3)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity0a\" DROP COLUMN \"attr\"", + "ALTER TABLE \"entity0b\" DROP COLUMN \"attr\"", "ALTER TABLE \"entity1\" DROP COLUMN \"attr\"")); + } + + @Test + public void deleteAttributeWithExpression() + { + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(attr.getExpression()).thenReturn("expression"); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + postgreSqlRepoCollection.deleteAttribute(entityMeta, attr); + verifyZeroInteractions(jdbcTemplate); + } + + @Test(expectedExceptions = UnknownAttributeException.class) + public void deleteAttributeUnknownAttribute() + { + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + postgreSqlRepoCollection.deleteAttribute(entityMeta, attr); } } \ No newline at end of file diff --git a/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollection.java b/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollection.java index c581d15733b..5e7c0f558d0 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollection.java +++ b/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollection.java @@ -49,15 +49,15 @@ default Stream> stream() /** * Get a repository by entity name * - * @throws UnknownEntityException + * @throws UnknownEntityException if no repository exists for the given entity name */ Repository getRepository(String name); /** * Get a repository for the given entity meta data * - * @param entityMeta - * @return + * @param entityMeta entity meta data + * @return repository for the given entity meta data */ Repository getRepository(EntityMetaData entityMeta); @@ -78,18 +78,18 @@ default Stream> stream() /** * Adds an Attribute to an EntityMeta * - * @param entityName - * @param attribute + * @param entityMeta entity meta data + * @param attribute attribute to add * @throws UnsupportedOperationException if this repository collection is not {@link RepositoryCollectionCapability#UPDATABLE} */ - void addAttribute(String entityName, AttributeMetaData attribute); + void addAttribute(EntityMetaData entityMeta, AttributeMetaData attribute); /** * Updates {@link Repository repositories} for the given updated attribute. * * @param entityMetaData entity meta data * @param attr attribute - * @param updatedAttr + * @param updatedAttr updated attribute * @throws UnsupportedOperationException if this repository collection is not {@link RepositoryCollectionCapability#UPDATABLE} */ void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr); @@ -97,16 +97,16 @@ default Stream> stream() /** * Removes an attribute from an entity * - * @param entityName - * @param attributeName + * @param entityMeta entity meta data + * @param attr attribute to remove * @throws UnsupportedOperationException if this repository collection is not {@link RepositoryCollectionCapability#UPDATABLE} */ - void deleteAttribute(String entityName, String attributeName); + void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr); /** * Returns the language codes defined in the meta data stored in this repository collection. * - * @return + * @return stream of language codes * @throws UnsupportedOperationException if this repository collection is not {@link RepositoryCollectionCapability#META_DATA_PERSISTABLE} */ Stream getLanguageCodes(); diff --git a/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollectionDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollectionDecorator.java index be1ae183b98..07d856f92df 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollectionDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/RepositoryCollectionDecorator.java @@ -92,9 +92,9 @@ public void deleteRepository(EntityMetaData entityMeta) } @Override - public void addAttribute(String entityName, AttributeMetaData attribute) + public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attribute) { - decoratedRepositoryCollection.addAttribute(entityName, attribute); + decoratedRepositoryCollection.addAttribute(entityMeta, attribute); } @Override @@ -104,9 +104,9 @@ public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData att } @Override - public void deleteAttribute(String entityName, String attributeName) + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) { - decoratedRepositoryCollection.deleteAttribute(entityName, attributeName); + decoratedRepositoryCollection.deleteAttribute(entityMeta, attr); } @Override diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java index 1ec801a6233..31b391e3297 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java @@ -385,22 +385,21 @@ private void updateEntity(EntityMetaData entityMeta) } Map currentAttrMap = StreamSupport - .stream(existingEntityMeta.getOwnAttributes().spliterator(), false) + .stream(existingEntityMeta.getOwnAllAttributes().spliterator(), false) .collect(toMap(AttributeMetaData::getName, Function.identity())); Map updateAttrMap = StreamSupport - .stream(entityMeta.getOwnAttributes().spliterator(), false) + .stream(entityMeta.getOwnAllAttributes().spliterator(), false) .collect(toMap(AttributeMetaData::getName, Function.identity())); // add attributes Set addedAttrNames = Sets.difference(updateAttrMap.keySet(), currentAttrMap.keySet()); if (!addedAttrNames.isEmpty()) { - String entityName = entityMeta.getName(); String backend = entityMeta.getBackend(); RepositoryCollection repoCollection = dataService.getMeta().getBackend(backend); addedAttrNames.stream().map(updateAttrMap::get).forEach(addedAttrEntity -> { - repoCollection.addAttribute(entityName, addedAttrEntity); + repoCollection.addAttribute(existingEntityMeta, addedAttrEntity); if (entityMeta.getName().equals(ENTITY_META_DATA)) { @@ -419,12 +418,11 @@ private void updateEntity(EntityMetaData entityMeta) if (!deletedAttrNames.isEmpty()) { - String entityName = entityMeta.getName(); String backend = entityMeta.getBackend(); RepositoryCollection repoCollection = dataService.getMeta().getBackend(backend); deletedAttrNames.forEach(deletedAttrName -> { - repoCollection.deleteAttribute(entityName, deletedAttrName); + repoCollection.deleteAttribute(existingEntityMeta, currentAttrMap.get(deletedAttrName)); if (entityMeta.getName().equals(ENTITY_META_DATA)) { diff --git a/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecorator.java index cb9ddcfea44..26ef0ee9904 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecorator.java @@ -50,10 +50,10 @@ public void deleteRepository(EntityMetaData entityMeta) } @Override - public void addAttribute(String entityFullName, AttributeMetaData attribute) + public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attribute) { - this.reindexActionRegisterService.register(entityFullName, UPDATE, METADATA, null); - this.decorated.addAttribute(entityFullName, attribute); + this.reindexActionRegisterService.register(entityMeta.getName(), UPDATE, METADATA, null); + this.decorated.addAttribute(entityMeta, attribute); } @Override @@ -64,10 +64,10 @@ public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData att } @Override - public void deleteAttribute(String entityFullName, String attributeName) + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) { - this.reindexActionRegisterService.register(entityFullName, UPDATE, METADATA, null); - this.decorated.deleteAttribute(entityFullName, attributeName); + this.reindexActionRegisterService.register(entityMeta.getName(), UPDATE, METADATA, null); + this.decorated.deleteAttribute(entityMeta, attr); } @Override diff --git a/molgenis-data/src/main/java/org/molgenis/data/support/AbstractRepositoryCollection.java b/molgenis-data/src/main/java/org/molgenis/data/support/AbstractRepositoryCollection.java index bb383e9bc35..c4afdfc68b4 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/support/AbstractRepositoryCollection.java +++ b/molgenis-data/src/main/java/org/molgenis/data/support/AbstractRepositoryCollection.java @@ -38,7 +38,7 @@ public void deleteRepository(EntityMetaData entityMeta) } @Override - public void addAttribute(String entityName, AttributeMetaData attribute) + public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attribute) { throw new UnsupportedOperationException(); } @@ -50,7 +50,7 @@ public void updateAttribute(EntityMetaData entityMetaData, AttributeMetaData att } @Override - public void deleteAttribute(String entityName, String attributeName) + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) { throw new UnsupportedOperationException(); } diff --git a/molgenis-data/src/test/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecoratorTest.java b/molgenis-data/src/test/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecoratorTest.java index 14a61d76466..3be7997ba7a 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecoratorTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/reindex/ReindexActionRepositoryCollectionDecoratorTest.java @@ -48,17 +48,20 @@ public void deleteEntityMeta() @Test public void addAttribute() { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn(REPOSITORY_NAME).getMock(); AttributeMetaData attribute = when(mock(AttributeMetaData.class).getName()).thenReturn("attribute").getMock(); - reindexActionRepositoryCollectionDecorator.addAttribute(REPOSITORY_NAME, attribute); - verify(decoratedRepositoryCollection, times(1)).addAttribute(REPOSITORY_NAME, attribute); + reindexActionRepositoryCollectionDecorator.addAttribute(entityMeta, attribute); + verify(decoratedRepositoryCollection, times(1)).addAttribute(entityMeta, attribute); verify(reindexActionRegisterService).register("repo", UPDATE, METADATA, null); } @Test public void deleteAttribute() { - reindexActionRepositoryCollectionDecorator.deleteAttribute(REPOSITORY_NAME, "attribute"); - verify(decoratedRepositoryCollection, times(1)).deleteAttribute(REPOSITORY_NAME, "attribute"); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn(REPOSITORY_NAME).getMock(); + AttributeMetaData attribute = when(mock(AttributeMetaData.class).getName()).thenReturn("attribute").getMock(); + reindexActionRepositoryCollectionDecorator.deleteAttribute(entityMeta, attribute); + verify(decoratedRepositoryCollection, times(1)).deleteAttribute(entityMeta, attribute); verify(reindexActionRegisterService).register("repo", UPDATE, METADATA, null); } From 74d4be8209ace13f8b2108b444588732b879ae6c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 09:40:22 +0200 Subject: [PATCH 07/49] ENH add enum options validation expression ENH add ref entity validation expression ENH allow user to update ref entity --- .../meta/model/AttributeMetaDataMetaData.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/AttributeMetaDataMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/AttributeMetaDataMetaData.java index 6bb90837ab1..8126e5f5548 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/AttributeMetaDataMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/AttributeMetaDataMetaData.java @@ -57,8 +57,8 @@ public void init() addAttribute(DATA_TYPE).setDataType(ENUM).setEnumOptions(AttributeType.getOptionsLowercase()).setNillable(false) .setLabel("Data type"); addAttribute(PARTS).setDataType(MREF).setRefEntity(this).setLabel("Attribute parts"); - addAttribute(REF_ENTITY).setDataType(XREF).setRefEntity(entityMetaMeta).setReadOnly(true) - .setLabel("Referenced entity"); + addAttribute(REF_ENTITY).setDataType(XREF).setRefEntity(entityMetaMeta).setLabel("Referenced entity") + .setValidationExpression(getRefEntityValidationExpression()); addAttribute(EXPRESSION).setNillable(true).setLabel("Expression") .setDescription("Computed value expression in Magma JavaScript"); addAttribute(NILLABLE).setDataType(BOOL).setNillable(false).setLabel("Nillable"); @@ -68,7 +68,8 @@ public void init() addAttribute(LABEL, ROLE_LOOKUP).setLabel("Label"); addAttribute(DESCRIPTION).setDataType(TEXT).setLabel("Description"); addAttribute(AGGREGATEABLE).setDataType(BOOL).setNillable(false).setLabel("Aggregatable"); - addAttribute(ENUM_OPTIONS).setDataType(TEXT).setLabel("Enum values").setDescription("For data type ENUM"); + addAttribute(ENUM_OPTIONS).setDataType(TEXT).setLabel("Enum values").setDescription("For data type ENUM") + .setValidationExpression(getEnumOptionsValidationExpression()); addAttribute(RANGE_MIN).setDataType(LONG).setLabel("Range min"); addAttribute(RANGE_MAX).setDataType(LONG).setLabel("Range max"); addAttribute(READ_ONLY).setDataType(BOOL).setNillable(false).setLabel("Read-only"); @@ -91,4 +92,21 @@ public void setEntityMetaDataMetaData(EntityMetaDataMetaData entityMetaMeta) { this.entityMetaMeta = requireNonNull(entityMetaMeta); } + + private static String getEnumOptionsValidationExpression() + { + return "$('" + ENUM_OPTIONS + "').isNull().and($('" + DATA_TYPE + "').eq('" + getValueString(ENUM) + + "').not()).or(" + "$('" + ENUM_OPTIONS + "').isNull().not().and($('" + DATA_TYPE + "').eq('" + + getValueString(ENUM) + "'))).value()"; + } + + private static String getRefEntityValidationExpression() + { + String regex = + "/^(" + getValueString(CATEGORICAL) + '|' + getValueString(CATEGORICAL_MREF) + '|' + getValueString( + FILE) + '|' + getValueString(MREF) + '|' + getValueString(XREF) + ")$/"; + + return "$('" + REF_ENTITY + "').isNull().and($('" + DATA_TYPE + "').matches(" + regex + ").not()).or(" + "$('" + + REF_ENTITY + "').isNull().not().and($('" + DATA_TYPE + "').matches(" + regex + "))).value()"; + } } From ac23435b9d5568d5ff357e47bab73eba5dcc7925 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 09:41:33 +0200 Subject: [PATCH 08/49] ENH add allowed attribute data type updates transitions fro decimal/int/long to string ENH add allowed attribute data type updates transitions fro int/long to bool --- .../data/meta/AttributeMetaDataRepositoryDecorator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java index 56a797522b8..aa3a1f4a7a9 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java @@ -374,7 +374,8 @@ private void validateUpdate(AttributeMetaData currentAttr, AttributeMetaData new DATA_TYPE_ALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.noneOf(AttributeType.class)); DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL, DATE_TIME, HTML, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL, DATE, HTML, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DECIMAL, EnumSet.of(CATEGORICAL, HTML, INT, LONG, SCRIPT, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS + .put(DECIMAL, EnumSet.of(CATEGORICAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); DATA_TYPE_ALLOWED_TRANSITIONS.put(ENUM, EnumSet.noneOf(AttributeType.class)); DATA_TYPE_ALLOWED_TRANSITIONS.put(FILE, EnumSet.noneOf(AttributeType.class)); @@ -383,8 +384,10 @@ private void validateUpdate(AttributeMetaData currentAttr, AttributeMetaData new DATA_TYPE_ALLOWED_TRANSITIONS.put(HTML, EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(INT, EnumSet.of(CATEGORICAL, DECIMAL, HTML, LONG, SCRIPT, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(LONG, EnumSet.of(CATEGORICAL, DECIMAL, HTML, INT, SCRIPT, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS + .put(INT, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, LONG, SCRIPT, STRING, TEXT, XREF)); + DATA_TYPE_ALLOWED_TRANSITIONS + .put(LONG, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, INT, SCRIPT, STRING, TEXT, XREF)); DATA_TYPE_ALLOWED_TRANSITIONS.put(MREF, EnumSet.of(CATEGORICAL_MREF)); // not implemented: SCRIPT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF // not allowed : SCRIPT -> COMPOUND, FILE, SCRIPT From 2cba23a095a3b34deb8861dc85907cd68fa77524 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 09:44:18 +0200 Subject: [PATCH 09/49] Add 'and' operator for use in JavaScript Magma scripts --- .../resources/js/molgenis-script-evaluator.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/molgenis-js/src/main/resources/js/molgenis-script-evaluator.js b/molgenis-js/src/main/resources/js/molgenis-script-evaluator.js index 5a99c2b0770..53d3dd7225f 100644 --- a/molgenis-js/src/main/resources/js/molgenis-script-evaluator.js +++ b/molgenis-js/src/main/resources/js/molgenis-script-evaluator.js @@ -278,6 +278,21 @@ function evalScript(script, entity) { this.val = (this.val || other.value()); return this; }, + /** + * Checks if something is one value and the other + * + * Example: $('female').and($('pregnant')).value() + * + * @param other : + * Another value + * + * @memberof $ + * @method and + */ + and: function (other) { + this.val = (this.val && other.value()); + return this; + }, /** * Returns true or false if Greater then the submitted value * From 24695dc110972f03f144683a2067e9aa44e84c14 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 09:53:52 +0200 Subject: [PATCH 10/49] FIx ignore representational data type changes --- .../PostgreSqlRepositoryCollection.java | 12 ++++++++ .../PostgreSqlRepositoryCollectionTest.java | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 0d2f88e85d5..d997df4e141 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -304,6 +304,18 @@ else if ((attr.getExpression() != null && updatedAttr.getExpression() == null) | private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { + // do nothing on representation changes XREF --> CATEGORICAL + if (isSingleReferenceType(attr) && isSingleReferenceType(updatedAttr)) + { + return; + } + + // do nothing on representation changes MREF --> CATEGORICAL_MREF + if (isMultipleReferenceType(attr) && isMultipleReferenceType(updatedAttr)) + { + return; + } + AttributeMetaData idAttr = entityMeta.getIdAttribute(); if (idAttr != null && idAttr.getName().equals(attr.getName())) { diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index d6c80948f6a..93cb32ff454 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -181,6 +181,34 @@ public void updateAttributeSingleRefDataTypeToDataType() throws Exception "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET DATA TYPE character varying(255) USING \"attr\"::character varying(255)")); } + @Test + public void updateAttributeSingleRefDataTypeToSingleRefDataType() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(XREF); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(CATEGORICAL); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verifyZeroInteractions(jdbcTemplate); + } + + @Test + public void updateAttributeMultiRefDataTypeToMultiRefDataType() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(MREF); + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(CATEGORICAL_MREF); + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + verifyZeroInteractions(jdbcTemplate); + } + @Test public void updateAttributeDataTypeToSingleRefDataType() throws Exception { From 0ae7bee4082f708a009e8a75670f5225f084de46 Mon Sep 17 00:00:00 2001 From: Fleur Kelpin Date: Tue, 16 Aug 2016 14:11:10 +0200 Subject: [PATCH 11/49] Fix #5185: Filtering on xref with INT id fails --- .../data/rsql/MolgenisRSQLVisitor.java | 48 +++++----- .../molgenis/data/rsql/RSQLValueParser.java | 94 +++++++++++++++++++ .../molgenis/data/rsql/MolgenisRSQLTest.java | 26 ++++- 3 files changed, 137 insertions(+), 31 deletions(-) create mode 100644 molgenis-data/src/main/java/org/molgenis/data/rsql/RSQLValueParser.java diff --git a/molgenis-data/src/main/java/org/molgenis/data/rsql/MolgenisRSQLVisitor.java b/molgenis-data/src/main/java/org/molgenis/data/rsql/MolgenisRSQLVisitor.java index 75519b4de1e..781134b8d55 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/rsql/MolgenisRSQLVisitor.java +++ b/molgenis-data/src/main/java/org/molgenis/data/rsql/MolgenisRSQLVisitor.java @@ -1,7 +1,5 @@ package org.molgenis.data.rsql; -import com.google.common.base.Function; -import com.google.common.collect.Iterables; import cz.jirutka.rsql.parser.ast.*; import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; @@ -11,15 +9,18 @@ import java.util.Iterator; import java.util.List; +import static java.util.stream.Collectors.toList; + /** - * RSQLVisitor implementation for molgenis Query. + * RSQLVisitor implementation that creates {@link Query} objects for an RSQL tree. * * @see https://github.com/jirutka/rsql-parser */ public class MolgenisRSQLVisitor extends NoArgRSQLVisitorAdapter> { - private final QueryImpl q = new QueryImpl(); + private final QueryImpl q = new QueryImpl<>(); private final EntityMetaData entityMetaData; + private final RSQLValueParser rsqlValueParser = new RSQLValueParser(); public MolgenisRSQLVisitor(EntityMetaData entityMetaData) { @@ -92,53 +93,46 @@ public Query visit(ComparisonNode node) } break; case "==": - Object eqValue = DataConverter.convert(values.get(0), getAttribute(node)); + Object eqValue = rsqlValueParser.parse(values.get(0), getAttribute(node)); q.eq(attrName, eqValue); break; case "=in=": AttributeMetaData inAttr = getAttribute(node); - q.in(attrName, Iterables.transform(values, new Function() - { - @Override - public Object apply(String value) - { - return DataConverter.convert(value, inAttr); - } - })); + q.in(attrName, values.stream().map(value -> rsqlValueParser.parse(value, inAttr)).collect(toList())); break; case "=lt=": case "<": AttributeMetaData ltAttr = getAttribute(node); - validateNumericOrDate(ltAttr, symbol); - Object ltValue = DataConverter.convert(values.get(0), ltAttr); + validateNumericOrDate(ltAttr); + Object ltValue = rsqlValueParser.parse(values.get(0), ltAttr); q.lt(attrName, ltValue); break; case "=le=": case "<=": AttributeMetaData leAttr = getAttribute(node); - validateNumericOrDate(leAttr, symbol); - Object leValue = DataConverter.convert(values.get(0), leAttr); + validateNumericOrDate(leAttr); + Object leValue = rsqlValueParser.parse(values.get(0), leAttr); q.le(attrName, leValue); break; case "=gt=": case ">": AttributeMetaData gtAttr = getAttribute(node); - validateNumericOrDate(gtAttr, symbol); - Object gtValue = DataConverter.convert(values.get(0), gtAttr); + validateNumericOrDate(gtAttr); + Object gtValue = rsqlValueParser.parse(values.get(0), gtAttr); q.gt(attrName, gtValue); break; case "=ge=": case ">=": AttributeMetaData geAttr = getAttribute(node); - validateNumericOrDate(geAttr, symbol); - Object geValue = DataConverter.convert(values.get(0), geAttr); + validateNumericOrDate(geAttr); + Object geValue = rsqlValueParser.parse(values.get(0), geAttr); q.ge(attrName, geValue); break; case "=rng=": AttributeMetaData rngAttr = getAttribute(node); - validateNumericOrDate(rngAttr, symbol); - Object fromValue = values.get(0) != null ? DataConverter.convert(values.get(0), rngAttr) : null; - Object toValue = values.get(1) != null ? DataConverter.convert(values.get(1), rngAttr) : null; + validateNumericOrDate(rngAttr); + Object fromValue = values.get(0) != null ? rsqlValueParser.parse(values.get(0), rngAttr) : null; + Object toValue = values.get(1) != null ? rsqlValueParser.parse(values.get(1), rngAttr) : null; q.rng(attrName, fromValue, toValue); break; case "=like=": @@ -146,7 +140,7 @@ public Object apply(String value) q.like(attrName, likeValue); break; case "!=": - Object notEqValue = DataConverter.convert(values.get(0), getAttribute(node)); + Object notEqValue = rsqlValueParser.parse(values.get(0), getAttribute(node)); q.not().eq(attrName, notEqValue); break; case "=should=": @@ -161,7 +155,7 @@ public Object apply(String value) return q; } - private void validateNumericOrDate(AttributeMetaData attr, String symbol) + private void validateNumericOrDate(AttributeMetaData attr) { switch (attr.getDataType()) { @@ -188,7 +182,7 @@ private AttributeMetaData getAttribute(ComparisonNode node) { throw new UnknownAttributeException("Unknown attribute [" + attrName + "]"); } - EntityMetaData entityMetaDataAtDepth = entityMetaData; + EntityMetaData entityMetaDataAtDepth; for (int i = 1; i < attrTokens.length; ++i) { entityMetaDataAtDepth = attr.getRefEntity(); diff --git a/molgenis-data/src/main/java/org/molgenis/data/rsql/RSQLValueParser.java b/molgenis-data/src/main/java/org/molgenis/data/rsql/RSQLValueParser.java new file mode 100644 index 00000000000..8c721f6ee83 --- /dev/null +++ b/molgenis-data/src/main/java/org/molgenis/data/rsql/RSQLValueParser.java @@ -0,0 +1,94 @@ +package org.molgenis.data.rsql; + +import org.molgenis.MolgenisFieldTypes.AttributeType; +import org.molgenis.data.MolgenisDataException; +import org.molgenis.data.meta.model.AttributeMetaData; +import org.molgenis.util.MolgenisDateFormat; +import org.springframework.stereotype.Service; + +import java.text.ParseException; +import java.util.Date; + +import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.molgenis.util.MolgenisDateFormat.getDateFormat; +import static org.molgenis.util.MolgenisDateFormat.getDateTimeFormat; + +/** + * Converts RSQL value symbols to the relevant data type. + */ +@Service +public class RSQLValueParser +{ + public Object parse(String valueString, AttributeMetaData attribute) + { + if (isEmpty(valueString)) + { + return null; + } + + AttributeType attrType = attribute.getDataType(); + switch (attrType) + { + case BOOL: + return Boolean.valueOf(valueString); + case EMAIL: + case ENUM: + case HTML: + case HYPERLINK: + case SCRIPT: + case STRING: + case TEXT: + return valueString; + case CATEGORICAL: + case XREF: + case FILE: + case CATEGORICAL_MREF: + case MREF: + return parse(valueString, attribute.getRefEntity().getIdAttribute()); + case DATE: + return convertDate(attribute, valueString); + case DATE_TIME: + return convertDateTime(attribute, valueString); + case DECIMAL: + return Double.valueOf(valueString); + case INT: + return Integer.valueOf(valueString); + case LONG: + return Long.valueOf(valueString); + case COMPOUND: + throw new RuntimeException(format("Illegal attribute type [%s]", attrType.toString())); + default: + throw new RuntimeException(format("Unknown attribute type [%s]", attrType.toString())); + } + } + + private static Date convertDateTime(AttributeMetaData attr, String paramValue) + { + try + { + return getDateTimeFormat().parse(paramValue); + } + catch (ParseException e) + { + throw new MolgenisDataException( + format("Attribute [%s] value [%s] does not match date format [%s]", attr.getName(), paramValue, + MolgenisDateFormat.DATEFORMAT_DATETIME)); + } + } + + private static Date convertDate(AttributeMetaData attr, String paramValue) + { + try + { + return getDateFormat().parse(paramValue); + } + catch (ParseException e) + { + throw new MolgenisDataException( + format("Attribute [%s] value [%s] does not match date format [%s].", attr.getName(), paramValue, + MolgenisDateFormat.DATEFORMAT_DATE)); + } + } + +} diff --git a/molgenis-data/src/test/java/org/molgenis/data/rsql/MolgenisRSQLTest.java b/molgenis-data/src/test/java/org/molgenis/data/rsql/MolgenisRSQLTest.java index 151572b2b95..2301fd8f6ba 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/rsql/MolgenisRSQLTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/rsql/MolgenisRSQLTest.java @@ -1,30 +1,31 @@ package org.molgenis.data.rsql; import cz.jirutka.rsql.parser.RSQLParserException; +import org.mockito.MockitoAnnotations; import org.molgenis.data.Entity; import org.molgenis.data.Query; import org.molgenis.data.UnknownAttributeException; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.data.support.QueryImpl; -import org.springframework.core.convert.ConversionFailedException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.molgenis.MolgenisFieldTypes.AttributeType.INT; -import static org.molgenis.MolgenisFieldTypes.AttributeType.STRING; +import static org.molgenis.MolgenisFieldTypes.AttributeType.*; import static org.testng.Assert.assertEquals; public class MolgenisRSQLTest { private MolgenisRSQL molgenisRSQL; private EntityMetaData entityMetaData; + private EntityMetaData genderEntityMetaData; @BeforeMethod public void beforeMethod() { + MockitoAnnotations.initMocks(this); molgenisRSQL = new MolgenisRSQL(); entityMetaData = when(mock(EntityMetaData.class).getName()).thenReturn("Person").getMock(); AttributeMetaData nameAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("name").getMock(); @@ -33,6 +34,16 @@ public void beforeMethod() when(ageAttr.getDataType()).thenReturn(INT); when(entityMetaData.getAttribute("name")).thenReturn(nameAttr); when(entityMetaData.getAttribute("age")).thenReturn(ageAttr); + + genderEntityMetaData = when(mock(EntityMetaData.class).getName()).thenReturn("Gender").getMock(); + AttributeMetaData genderIdAttribute = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(genderIdAttribute.getDataType()).thenReturn(INT); + when(genderEntityMetaData.getIdAttribute()).thenReturn(genderIdAttribute); + AttributeMetaData genderAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("gender").getMock(); + when(genderAttr.getDataType()).thenReturn(XREF); + when(genderAttr.getRefEntity()).thenReturn(genderEntityMetaData); + when(entityMetaData.getAttribute("gender")).thenReturn(genderAttr); + } @Test @@ -112,7 +123,7 @@ public void testGreaterThanOnNonNumericalAttribute() throws RSQLParserException molgenisRSQL.createQuery("name>87", entityMetaData); } - @Test(expectedExceptions = ConversionFailedException.class) + @Test(expectedExceptions = NumberFormatException.class) public void testGreaterThanWithNonNumericalArg() throws RSQLParserException { molgenisRSQL.createQuery("age>bogus", entityMetaData); @@ -125,4 +136,11 @@ public void testComplexQuery() throws RSQLParserException assertEquals(q, new QueryImpl<>().nest().nest().eq("name", "piet").and().eq("age", 87).unnest().or().nest() .eq("name", "klaas").and().gt("age", 100).unnest().unnest()); } + + @Test + public void testXrefIntegerIdValue() + { + Query q = molgenisRSQL.createQuery("gender==2", entityMetaData); + assertEquals(q, new QueryImpl<>().eq("gender", 2)); + } } From bdfd584f8ffe4d2adc4d4472e1b992ce65008296 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 14:34:02 +0200 Subject: [PATCH 12/49] Resolve IntelliJ warnings, reuse code, add tests --- .../postgresql/PostgreSqlQueryGenerator.java | 253 ++++++++++++------ .../PostgreSqlQueryGeneratorTest.java | 191 ++++++++++++- 2 files changed, 349 insertions(+), 95 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index a33ba257b83..7ebc5866f30 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -1,5 +1,6 @@ package org.molgenis.data.postgresql; +import autovalue.shaded.com.google.common.common.collect.Lists; import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.*; import org.molgenis.data.QueryRule.Operator; @@ -8,7 +9,6 @@ import org.molgenis.data.support.QueryImpl; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -16,6 +16,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; import static java.util.stream.IntStream.range; import static java.util.stream.StreamSupport.stream; import static org.molgenis.MolgenisFieldTypes.AttributeType.BOOL; @@ -33,12 +34,16 @@ private PostgreSqlQueryGenerator() } - static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + private static String getSqlConstraintPrimaryKey(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder strBuilder = new StringBuilder(); - strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD CONSTRAINT ") - .append(getForeignKeyName(entityMeta, attr)).append(" FOREIGN KEY (").append(getColumnName(attr)) - .append(") REFERENCES ").append(getTableName(attr.getRefEntity())).append('(') + return "CONSTRAINT " + getPrimaryKeyName(entityMeta, attr) + " PRIMARY KEY (" + getColumnName(attr) + ')'; + } + + private static String getSqlForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + StringBuilder strBuilder = new StringBuilder("CONSTRAINT ").append(getForeignKeyName(entityMeta, attr)) + .append(" FOREIGN KEY (").append(getColumnName(attr)).append(") REFERENCES ") + .append(getTableName(attr.getRefEntity())).append('(') .append(getColumnName(attr.getRefEntity().getIdAttribute())).append(')'); // for self-referencing data defer checking constraints until the end of the transaction @@ -49,88 +54,125 @@ static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaDat return strBuilder.toString(); } + private static String getSqlUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "CONSTRAINT " + getUniqueKeyName(entityMeta, attr) + " UNIQUE (" + getColumnName(attr) + ')'; + } + + private static String getSqlCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (attr.getDataType() != ENUM) + { + throw new MolgenisDataException( + format("Check constraint only allowed for attribute type [%s]", ENUM.toString())); + } + + return "CONSTRAINT " + getCheckConstraintName(entityMeta, attr) + " CHECK (" + getColumnName(attr) + " IN (" + + attr.getEnumOptions().stream().map(enumOption -> '\'' + enumOption + '\'').collect(joining(",")) + + "))"; + } + + static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlForeignKey(entityMeta, attr); + } + static String getSqlDropForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder strBuilder = new StringBuilder(); - strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP CONSTRAINT ") - .append(getForeignKeyName(entityMeta, attr)); - return strBuilder.toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getForeignKeyName(entityMeta, attr); } static String getSqlCreateUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { - // PostgreSQL name convention - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD CONSTRAINT ") - .append(getUniqueKeyName(entityMeta, attr)).append(" UNIQUE (").append(getColumnName(attr)).append(')') - .toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlUniqueKey(entityMeta, attr); } static String getSqlDropUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { - // PostgreSQL name convention - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP CONSTRAINT ") - .append(getUniqueKeyName(entityMeta, attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getUniqueKeyName(entityMeta, attr); + } + + static String getSqlCreateCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlCheckConstraint(entityMeta, attr); + } + + static String getSqlDropCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (attr.getDataType() != ENUM) + { + throw new MolgenisDataException( + format("Check constraint only allowed for attribute type [%s]", ENUM.toString())); + } + + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getCheckConstraintName(entityMeta, + attr); } static String getSqlSetNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" SET NOT NULL").toString(); + if (!attr.isNillable()) + { + throw new MolgenisDataException( + format("Entity [%s] attribute [%s] is not nillable", entityMeta.getName(), attr.getName())); + } + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " SET NOT NULL"; } static String getSqlDropNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" DROP NOT NULL").toString(); + if (attr.isNillable()) + { + throw new MolgenisDataException( + format("Entity [%s] attribute [%s] is nillable", entityMeta.getName(), attr.getName())); + } + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " DROP NOT NULL"; } static String getSqlSetDataType(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" SET DATA TYPE ").append(getPostgreSqlType(attr)).append(" USING ") - .append(getColumnName(attr)).append("::").append(getPostgreSqlType(attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " SET DATA TYPE " + + getPostgreSqlType(attr) + " USING " + getColumnName(attr) + "::" + getPostgreSqlType(attr); } static String getSqlAddColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder sql = new StringBuilder(); - sql.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD "); - getSqlAttribute(sql, attr); + StringBuilder sql = new StringBuilder("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD "); + sql.append(getSqlColumn(entityMeta, attr)); + List sqlTableConstraints = getSqlTableConstraints(entityMeta, attr); + if (!sqlTableConstraints.isEmpty()) + { + sqlTableConstraints.forEach(sqlTableConstraint -> sql.append(",ADD ").append(sqlTableConstraint)); + } return sql.toString(); } static String getSqlCreateTable(EntityMetaData entityMeta) { - StringBuilder sql = new StringBuilder(); - sql.append("CREATE TABLE ").append(getTableName(entityMeta)).append('('); - - getPersistedAttributesNonMref(entityMeta).forEach(attr -> - { - getSqlAttribute(sql, attr); - sql.append(", "); - }); + List persistedNonMrefAttrs = getPersistedAttributesNonMref(entityMeta).collect(toList()); - // primary key is first attribute unless otherwise indicated - AttributeMetaData idAttribute = entityMeta.getIdAttribute(); + StringBuilder sql = new StringBuilder("CREATE TABLE ").append(getTableName(entityMeta)).append('('); - if (idAttribute == null) + // add columns + for (Iterator it = persistedNonMrefAttrs.iterator(); it.hasNext(); ) { - throw new MolgenisDataException(format("Missing idAttribute for entity [%s]", entityMeta.getName())); - } - - if (isReferenceType(idAttribute)) - { - throw new RuntimeException(format("primary key(%s.%s) cannot be XREF or MREF", getTableName(entityMeta), - getColumnName(idAttribute))); + sql.append(getSqlColumn(entityMeta, it.next())); + if (it.hasNext()) + { + sql.append(','); + } } - if (idAttribute.isNillable()) + // add table constraints + for (AttributeMetaData persistedNonMrefAttr : persistedNonMrefAttrs) { - throw new RuntimeException(format("idAttribute (%s.%s) should not be nillable", getTableName(entityMeta), - getColumnName(idAttribute))); + List sqlTableConstraints = getSqlTableConstraints(entityMeta, persistedNonMrefAttr); + if (!sqlTableConstraints.isEmpty()) + { + sqlTableConstraints.forEach(sqlTableConstraint -> sql.append(',').append(sqlTableConstraint)); + } } - sql.append("PRIMARY KEY (").append(getColumnName(entityMeta.getIdAttribute())).append(')'); sql.append(')'); return sql.toString(); @@ -139,9 +181,8 @@ static String getSqlCreateTable(EntityMetaData entityMeta) static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { AttributeMetaData idAttr = entityMeta.getIdAttribute(); - StringBuilder sql = new StringBuilder(); - - sql.append("CREATE TABLE IF NOT EXISTS ").append(getJunctionTableName(entityMeta, attr)).append(" (") + StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS ") + .append(getJunctionTableName(entityMeta, attr)).append(" (") .append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(" INT,").append(getColumnName(idAttr)) .append(' ').append(getPostgreSqlType(idAttr)).append(" NOT NULL, ").append(getColumnName(attr)) .append(' ').append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())) @@ -197,14 +238,12 @@ static String getSqlDropTable(EntityMetaData entityMeta) static String getSqlDropColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP COLUMN ") - .append(getColumnName(attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP COLUMN " + getColumnName(attr); } static String getSqlInsert(EntityMetaData entityMeta) { - StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO ").append(getTableName(entityMeta)).append(" ("); + StringBuilder sql = new StringBuilder("INSERT INTO ").append(getTableName(entityMeta)).append(" ("); StringBuilder params = new StringBuilder(); getPersistedAttributesNonMref(entityMeta).forEach(attr -> { @@ -222,14 +261,14 @@ static String getSqlInsert(EntityMetaData entityMeta) static String getSqlInsertMref(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData idAttr) { - return new StringBuilder().append("INSERT INTO ").append(getJunctionTableName(entityMeta, attr)).append(" (") - .append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(',').append(getColumnName(idAttr)) - .append(',').append(getColumnName(attr)).append(") VALUES (?,?,?)").toString(); + return "INSERT INTO " + getJunctionTableName(entityMeta, attr) + " (" + getColumnName( + JUNCTION_TABLE_ORDER_ATTR_NAME) + ',' + getColumnName(idAttr) + ',' + getColumnName(attr) + + ") VALUES (?,?,?)"; } static String getSqlDeleteAll(EntityMetaData entityMeta) { - return new StringBuilder().append("DELETE FROM ").append(getTableName(entityMeta)).toString(); + return "DELETE FROM " + getTableName(entityMeta); } static String getSqlDelete(EntityMetaData entityMeta) @@ -239,8 +278,7 @@ static String getSqlDelete(EntityMetaData entityMeta) static String getSqlDelete(String tableName, AttributeMetaData attr) { - return new StringBuilder().append("DELETE FROM ").append(tableName).append(" WHERE ") - .append(getColumnName(attr)).append(" = ?").toString(); + return "DELETE FROM " + tableName + " WHERE " + getColumnName(attr) + " = ?"; } static String getJunctionTableSelect(EntityMetaData entityMeta, AttributeMetaData attr, int numOfIds) @@ -371,16 +409,16 @@ static String getSqlCount(EntityMetaData entityMeta, Query sqlBuilder.append("(*)"); } - String from = getSqlFromForCount(entityMeta, q, mrefAttrsInQuery); + String from = getSqlFromForCount(entityMeta, mrefAttrsInQuery); String where = getSqlWhere(entityMeta, q, parameters, 0); sqlBuilder.append(from).append(" WHERE ").append(where); } return sqlBuilder.toString(); } - private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) + private static String getSqlColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - sql.append(getColumnName(attr)).append(' '); + StringBuilder sqlBuilder = new StringBuilder(getColumnName(attr)).append(' '); AttributeType attrType = attr.getDataType(); switch (attrType) @@ -398,12 +436,12 @@ private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) case SCRIPT: case STRING: case TEXT: - sql.append(getPostgreSqlType(attr)); + sqlBuilder.append(getPostgreSqlType(attr)); break; case CATEGORICAL: case FILE: case XREF: - sql.append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())); + sqlBuilder.append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())); break; case COMPOUND: case CATEGORICAL_MREF: @@ -413,22 +451,57 @@ private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) throw new RuntimeException(format("Unknown attribute type [%s]", attrType.toString())); } - if (!attr.isNillable()) + String sqlColumnConstraints = getSqlColumnConstraints(entityMeta, attr); + if (!sqlColumnConstraints.isEmpty()) { - sql.append(" NOT NULL"); + sqlBuilder.append(' ').append(sqlColumnConstraints); } + return sqlBuilder.toString(); + } + + private static String getSqlColumnConstraints(EntityMetaData entityMeta, AttributeMetaData attr) + { + StringBuilder sqlBuilder = new StringBuilder(); + if (!attr.getName().equals(entityMeta.getIdAttribute().getName())) + { + if (!attr.isNillable()) + { + sqlBuilder.append("NOT NULL"); + } + } + return sqlBuilder.toString(); + } + + private static List getSqlTableConstraints(EntityMetaData entityMeta, AttributeMetaData attr) + { + List tableConstraints = Lists.newArrayList(); - if (attr.getDataType() == ENUM) + if (attr.getName().equals(entityMeta.getIdAttribute().getName())) { - sql.append(" CHECK (").append(getColumnName(attr)).append(" IN (") - .append(attr.getEnumOptions().stream().map(enumOption -> '\'' + enumOption + '\'') - .collect(joining(","))).append("))"); + tableConstraints.add(getSqlConstraintPrimaryKey(entityMeta, attr)); } + else + { + if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) + { + tableConstraints.add(getSqlForeignKey(entityMeta, attr)); + } + if (attr.isUnique()) + { + tableConstraints.add(getSqlUniqueKey(entityMeta, attr)); + } + if (attr.getDataType() == ENUM) + { + tableConstraints.add(getSqlCheckConstraint(entityMeta, attr)); + } + } + + return tableConstraints; } private static String getSqlDropTable(String tableName) { - return new StringBuilder("DROP TABLE ").append(tableName).toString(); + return "DROP TABLE " + tableName; } private static String getSqlWhere(EntityMetaData entityMeta, Query q, List parameters, @@ -696,7 +769,7 @@ private static String getSqlSort(EntityMetaData entityMeta, Q private static String getSqlFrom(EntityMetaData entityMeta, Query q) { - StringBuilder from = new StringBuilder().append(" FROM ").append(getTableName(entityMeta)).append(" AS this"); + StringBuilder from = new StringBuilder(" FROM ").append(getTableName(entityMeta)).append(" AS this"); AttributeMetaData idAttribute = entityMeta.getIdAttribute(); @@ -715,10 +788,10 @@ private static String getSqlFrom(EntityMetaData entityMeta, Q return from.toString(); } - private static String getSqlFromForCount(EntityMetaData entityMeta, Query q, + private static String getSqlFromForCount(EntityMetaData entityMeta, List mrefAttrsInQuery) { - StringBuilder from = new StringBuilder().append(" FROM ").append(getTableName(entityMeta)).append(" AS this"); + StringBuilder from = new StringBuilder(" FROM ").append(getTableName(entityMeta)).append(" AS this"); AttributeMetaData idAttribute = entityMeta.getIdAttribute(); @@ -743,34 +816,42 @@ private static String getColumnName(AttributeMetaData attr) private static String getColumnName(String attrName) { - return new StringBuilder().append('"').append(attrName).append('"').toString(); + return "\"" + attrName + '"'; } private static String getFilterColumnName(AttributeMetaData attr, int filterIndex) { - return new StringBuilder().append('"').append(attr.getName()).append("_filter").append(filterIndex).append('"') - .toString(); + return "\"" + attr.getName() + "_filter" + filterIndex + '"'; + } + + private static String getPrimaryKeyName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getConstraintName(entityMeta, attr, "pkey"); } private static String getForeignKeyName(EntityMetaData entityMeta, AttributeMetaData attr) { - return getKeyName(entityMeta, attr, "fkey"); + return getConstraintName(entityMeta, attr, "fkey"); } private static String getUniqueKeyName(EntityMetaData entityMeta, AttributeMetaData attr) { - return getKeyName(entityMeta, attr, "key"); + return getConstraintName(entityMeta, attr, "key"); + } + + private static String getCheckConstraintName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getConstraintName(entityMeta, attr, "chk"); } - private static String getKeyName(EntityMetaData entityMeta, AttributeMetaData attr, String keyPostfix) + private static String getConstraintName(EntityMetaData entityMeta, AttributeMetaData attr, String constraintPostfix) { - return new StringBuilder().append('"').append(entityMeta.getName()).append('_').append(attr.getName()) - .append('_').append(keyPostfix).append('"').toString(); + return "\"" + entityMeta.getName() + '_' + attr.getName() + '_' + constraintPostfix + '"'; } private static List getMrefQueryAttrs(EntityMetaData entityMeta, Query q) { - List mrefAttrsInQuery = new ArrayList<>(); + List mrefAttrsInQuery = Lists.newArrayList(); getMrefQueryFieldsRec(entityMeta, q.getRules(), mrefAttrsInQuery); return mrefAttrsInQuery; } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index da292c47261..c9c424b8904 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -2,6 +2,7 @@ import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.Entity; +import org.molgenis.data.MolgenisDataException; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.data.meta.model.Package; @@ -24,6 +25,134 @@ public class PostgreSqlQueryGeneratorTest { + @Test + public void getSqlSetNotNull() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + assertEquals(PostgreSqlQueryGenerator.getSqlSetNotNull(entityMeta, attr), + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET NOT NULL"); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlSetNotNullAttrNotNillable() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(false); + PostgreSqlQueryGenerator.getSqlSetNotNull(entityMeta, attr); + } + + @Test + public void getSqlSetDataType() + { + + } + + @Test + public void getSqlDropNotNull() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(false); + assertEquals(PostgreSqlQueryGenerator.getSqlDropNotNull(entityMeta, attr), + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" DROP NOT NULL"); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlDropNotNullAttrNotNillable() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + PostgreSqlQueryGenerator.getSqlDropNotNull(entityMeta, attr); + } + + @Test + public void getSqlCreateTable() + { + // ref entity with string id attribute + AttributeMetaData refIdAttrStr = mock(AttributeMetaData.class); + when(refIdAttrStr.getName()).thenReturn("refIdAttrStr"); + when(refIdAttrStr.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMetaString = mock(EntityMetaData.class); + when(refEntityMetaString.getName()).thenReturn("refEntityStr"); + when(refEntityMetaString.getIdAttribute()).thenReturn(refIdAttrStr); + + // ref entity with int id attribute + AttributeMetaData refIdAttrInt = mock(AttributeMetaData.class); + when(refIdAttrInt.getName()).thenReturn("refIdAttrInt"); + when(refIdAttrInt.getDataType()).thenReturn(INT); + EntityMetaData refEntityMetaInt = mock(EntityMetaData.class); + when(refEntityMetaInt.getName()).thenReturn("refEntityInt"); + when(refEntityMetaInt.getIdAttribute()).thenReturn(refIdAttrInt); + + // entity with attributes of all types and flavors + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(idAttr.getDataType()).thenReturn(STRING); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + + List atomicAttrs = Lists.newArrayList(); + atomicAttrs.add(idAttr); + StringBuilder attrNameBuilder = new StringBuilder(16); + for (boolean hasExpression : newArrayList(false, true)) + { + for (boolean unique : newArrayList(false, true)) + { + for (boolean nillable : newArrayList(false, true)) + { + for (AttributeType attrType : AttributeType.values()) + { + if (attrType != COMPOUND) + { + attrNameBuilder.setLength(0); + attrNameBuilder.append(attrType.toString().toLowerCase()); + if (hasExpression) + { + attrNameBuilder.append("_expression"); + } + if (unique) + { + attrNameBuilder.append("_unique"); + } + if (nillable) + { + attrNameBuilder.append("_nillable"); + } + + AttributeMetaData attr = mock(AttributeMetaData.class); + when(attr.getName()).thenReturn(attrNameBuilder.toString()); + when(attr.getDataType()).thenReturn(attrType); + when(attr.getExpression()).thenReturn(hasExpression ? "expression" : null); + when(attr.isUnique()).thenReturn(unique); + when(attr.isNillable()).thenReturn(nillable); + + if (attrType == CATEGORICAL || attrType == CATEGORICAL_MREF) + { + when(attr.getRefEntity()).thenReturn(refEntityMetaString); + } + else if (attrType == FILE || attrType == XREF || attrType == MREF) + { + when(attr.getRefEntity()).thenReturn(refEntityMetaInt); + } + else if (attrType == ENUM) + { + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1")); + } + atomicAttrs.add(attr); + } + } + } + } + } + when(entityMeta.getAtomicAttributes()).thenReturn(atomicAttrs); + + assertEquals(PostgreSqlQueryGenerator.getSqlCreateTable(entityMeta), + "CREATE TABLE \"entity\"(\"id\" character varying(255),\"bool\" boolean NOT NULL,\"categorical\" character varying(255) NOT NULL,\"date\" date NOT NULL,\"date_time\" timestamp NOT NULL,\"decimal\" double precision NOT NULL,\"email\" character varying(255) NOT NULL,\"enum\" character varying(255) NOT NULL,\"file\" integer NOT NULL,\"html\" text NOT NULL,\"hyperlink\" character varying(255) NOT NULL,\"int\" integer NOT NULL,\"long\" bigint NOT NULL,\"script\" text NOT NULL,\"string\" character varying(255) NOT NULL,\"text\" text NOT NULL,\"xref\" integer NOT NULL,\"bool_nillable\" boolean,\"categorical_nillable\" character varying(255),\"date_nillable\" date,\"date_time_nillable\" timestamp,\"decimal_nillable\" double precision,\"email_nillable\" character varying(255),\"enum_nillable\" character varying(255),\"file_nillable\" integer,\"html_nillable\" text,\"hyperlink_nillable\" character varying(255),\"int_nillable\" integer,\"long_nillable\" bigint,\"script_nillable\" text,\"string_nillable\" character varying(255),\"text_nillable\" text,\"xref_nillable\" integer,\"bool_unique\" boolean NOT NULL,\"categorical_unique\" character varying(255) NOT NULL,\"date_unique\" date NOT NULL,\"date_time_unique\" timestamp NOT NULL,\"decimal_unique\" double precision NOT NULL,\"email_unique\" character varying(255) NOT NULL,\"enum_unique\" character varying(255) NOT NULL,\"file_unique\" integer NOT NULL,\"html_unique\" text NOT NULL,\"hyperlink_unique\" character varying(255) NOT NULL,\"int_unique\" integer NOT NULL,\"long_unique\" bigint NOT NULL,\"script_unique\" text NOT NULL,\"string_unique\" character varying(255) NOT NULL,\"text_unique\" text NOT NULL,\"xref_unique\" integer NOT NULL,\"bool_unique_nillable\" boolean,\"categorical_unique_nillable\" character varying(255),\"date_unique_nillable\" date,\"date_time_unique_nillable\" timestamp,\"decimal_unique_nillable\" double precision,\"email_unique_nillable\" character varying(255),\"enum_unique_nillable\" character varying(255),\"file_unique_nillable\" integer,\"html_unique_nillable\" text,\"hyperlink_unique_nillable\" character varying(255),\"int_unique_nillable\" integer,\"long_unique_nillable\" bigint,\"script_unique_nillable\" text,\"string_unique_nillable\" character varying(255),\"text_unique_nillable\" text,\"xref_unique_nillable\" integer,CONSTRAINT \"entity_id_pkey\" PRIMARY KEY (\"id\"),CONSTRAINT \"entity_categorical_fkey\" FOREIGN KEY (\"categorical\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_enum_chk\" CHECK (\"enum\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_fkey\" FOREIGN KEY (\"file\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_fkey\" FOREIGN KEY (\"xref\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_categorical_nillable_fkey\" FOREIGN KEY (\"categorical_nillable\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_enum_nillable_chk\" CHECK (\"enum_nillable\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_nillable_fkey\" FOREIGN KEY (\"file_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_nillable_fkey\" FOREIGN KEY (\"xref_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_bool_unique_key\" UNIQUE (\"bool_unique\"),CONSTRAINT \"entity_categorical_unique_fkey\" FOREIGN KEY (\"categorical_unique\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_categorical_unique_key\" UNIQUE (\"categorical_unique\"),CONSTRAINT \"entity_date_unique_key\" UNIQUE (\"date_unique\"),CONSTRAINT \"entity_date_time_unique_key\" UNIQUE (\"date_time_unique\"),CONSTRAINT \"entity_decimal_unique_key\" UNIQUE (\"decimal_unique\"),CONSTRAINT \"entity_email_unique_key\" UNIQUE (\"email_unique\"),CONSTRAINT \"entity_enum_unique_key\" UNIQUE (\"enum_unique\"),CONSTRAINT \"entity_enum_unique_chk\" CHECK (\"enum_unique\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_unique_fkey\" FOREIGN KEY (\"file_unique\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_file_unique_key\" UNIQUE (\"file_unique\"),CONSTRAINT \"entity_html_unique_key\" UNIQUE (\"html_unique\"),CONSTRAINT \"entity_hyperlink_unique_key\" UNIQUE (\"hyperlink_unique\"),CONSTRAINT \"entity_int_unique_key\" UNIQUE (\"int_unique\"),CONSTRAINT \"entity_long_unique_key\" UNIQUE (\"long_unique\"),CONSTRAINT \"entity_script_unique_key\" UNIQUE (\"script_unique\"),CONSTRAINT \"entity_string_unique_key\" UNIQUE (\"string_unique\"),CONSTRAINT \"entity_text_unique_key\" UNIQUE (\"text_unique\"),CONSTRAINT \"entity_xref_unique_fkey\" FOREIGN KEY (\"xref_unique\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_unique_key\" UNIQUE (\"xref_unique\"),CONSTRAINT \"entity_bool_unique_nillable_key\" UNIQUE (\"bool_unique_nillable\"),CONSTRAINT \"entity_categorical_unique_nillable_fkey\" FOREIGN KEY (\"categorical_unique_nillable\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_categorical_unique_nillable_key\" UNIQUE (\"categorical_unique_nillable\"),CONSTRAINT \"entity_date_unique_nillable_key\" UNIQUE (\"date_unique_nillable\"),CONSTRAINT \"entity_date_time_unique_nillable_key\" UNIQUE (\"date_time_unique_nillable\"),CONSTRAINT \"entity_decimal_unique_nillable_key\" UNIQUE (\"decimal_unique_nillable\"),CONSTRAINT \"entity_email_unique_nillable_key\" UNIQUE (\"email_unique_nillable\"),CONSTRAINT \"entity_enum_unique_nillable_key\" UNIQUE (\"enum_unique_nillable\"),CONSTRAINT \"entity_enum_unique_nillable_chk\" CHECK (\"enum_unique_nillable\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_unique_nillable_fkey\" FOREIGN KEY (\"file_unique_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_file_unique_nillable_key\" UNIQUE (\"file_unique_nillable\"),CONSTRAINT \"entity_html_unique_nillable_key\" UNIQUE (\"html_unique_nillable\"),CONSTRAINT \"entity_hyperlink_unique_nillable_key\" UNIQUE (\"hyperlink_unique_nillable\"),CONSTRAINT \"entity_int_unique_nillable_key\" UNIQUE (\"int_unique_nillable\"),CONSTRAINT \"entity_long_unique_nillable_key\" UNIQUE (\"long_unique_nillable\"),CONSTRAINT \"entity_script_unique_nillable_key\" UNIQUE (\"script_unique_nillable\"),CONSTRAINT \"entity_string_unique_nillable_key\" UNIQUE (\"string_unique_nillable\"),CONSTRAINT \"entity_text_unique_nillable_key\" UNIQUE (\"text_unique_nillable\"),CONSTRAINT \"entity_xref_unique_nillable_fkey\" FOREIGN KEY (\"xref_unique_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_unique_nillable_key\" UNIQUE (\"xref_unique_nillable\"))"); + } + @Test public void getSqlCreateForeignKey() { @@ -94,6 +223,46 @@ public void getSqlDropUniqueKey() assertEquals(PostgreSqlQueryGenerator.getSqlDropUniqueKey(entityMeta, attr), expectedSql); } + @Test + public void getSqlCreateCheckConstraint() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(ENUM); + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1", "enum2")); + assertEquals(PostgreSqlQueryGenerator.getSqlCreateCheckConstraint(entityMeta, attr), + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0','enum1','enum2'))"); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlCreateCheckConstraintWrongDataType() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + PostgreSqlQueryGenerator.getSqlCreateCheckConstraint(entityMeta, attr); + } + + @Test + public void getSqlDropCheckConstraint() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(ENUM); + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1", "enum2")); + assertEquals(PostgreSqlQueryGenerator.getSqlDropCheckConstraint(entityMeta, attr), + "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_chk\""); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlDropCheckConstraintWrongDataType() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + PostgreSqlQueryGenerator.getSqlDropCheckConstraint(entityMeta, attr); + } + @Test public void getSqlCreateJunctionTable() { @@ -130,7 +299,7 @@ public void getSqlCreateJunctionTableSelfReferencing() } @Test - public void testGetSqlSelectMref() throws Exception + public void getSqlSelectMref() throws Exception { Package package_ = when(mock(Package.class).getName()).thenReturn("org_molgenis").getMock(); @@ -197,15 +366,16 @@ public static Iterator getSqlAddColumnProvider() when(refEntityMetaInt.getIdAttribute()).thenReturn(refIdAttrInt); return Arrays.asList(new Object[] { BOOL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" boolean" }, - new Object[] { CATEGORICAL, true, refEntityMetaInt, "ALTER TABLE \"entity\" ADD \"attr\" integer" }, + new Object[] { CATEGORICAL, true, refEntityMetaInt, + "ALTER TABLE \"entity\" ADD \"attr\" integer,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\")" }, new Object[] { DATE, true, null, "ALTER TABLE \"entity\" ADD \"attr\" date" }, new Object[] { DATE_TIME, true, null, "ALTER TABLE \"entity\" ADD \"attr\" timestamp" }, new Object[] { DECIMAL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" double precision" }, new Object[] { EMAIL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { ENUM, true, null, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) CHECK (\"attr\" IN ('enum0, enum1'))" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0, enum1'))" }, new Object[] { FILE, true, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { HTML, true, null, "ALTER TABLE \"entity\" ADD \"attr\" text" }, new Object[] { HYPERLINK, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { INT, true, null, "ALTER TABLE \"entity\" ADD \"attr\" integer" }, @@ -214,19 +384,19 @@ public static Iterator getSqlAddColumnProvider() new Object[] { STRING, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { TEXT, true, null, "ALTER TABLE \"entity\" ADD \"attr\" text" }, new Object[] { XREF, true, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { BOOL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" boolean NOT NULL" }, new Object[] { CATEGORICAL, false, refEntityMetaInt, - "ALTER TABLE \"entity\" ADD \"attr\" integer NOT NULL" }, + "ALTER TABLE \"entity\" ADD \"attr\" integer NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\")" }, new Object[] { DATE, false, null, "ALTER TABLE \"entity\" ADD \"attr\" date NOT NULL" }, new Object[] { DATE_TIME, false, null, "ALTER TABLE \"entity\" ADD \"attr\" timestamp NOT NULL" }, new Object[] { DECIMAL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" double precision NOT NULL" }, new Object[] { EMAIL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, new Object[] { ENUM, false, null, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL CHECK (\"attr\" IN ('enum0, enum1'))" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0, enum1'))" }, new Object[] { FILE, false, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { HTML, false, null, "ALTER TABLE \"entity\" ADD \"attr\" text NOT NULL" }, new Object[] { HYPERLINK, false, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, @@ -237,13 +407,16 @@ public static Iterator getSqlAddColumnProvider() "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, new Object[] { TEXT, false, null, "ALTER TABLE \"entity\" ADD \"attr\" text NOT NULL" }, new Object[] { XREF, false, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }).iterator(); + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }) + .iterator(); } @Test(dataProvider = "getSqlAddColumnProvider") public void getSqlAddColumn(AttributeType attrType, boolean nillable, EntityMetaData refEntityMeta, String sql) { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); when(attr.getDataType()).thenReturn(attrType); when(attr.isNillable()).thenReturn(nillable); From 9d071bd17b9d4032b5daa6fe58a7b282ffd746f0 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 14:35:08 +0200 Subject: [PATCH 13/49] Add tests, reuse code, add documentation --- .../PostgreSqlRepositoryCollection.java | 336 +++++++++++------- .../PostgreSqlRepositoryCollectionTest.java | 14 + 2 files changed, 217 insertions(+), 133 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index d997df4e141..2ffffe3868c 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -23,12 +23,14 @@ import static java.util.EnumSet.of; import static java.util.Objects.requireNonNull; import static org.molgenis.MolgenisFieldTypes.AttributeType.COMPOUND; +import static org.molgenis.MolgenisFieldTypes.AttributeType.ENUM; import static org.molgenis.data.RepositoryCollectionCapability.*; import static org.molgenis.data.i18n.model.LanguageMetaData.*; import static org.molgenis.data.meta.MetaUtils.getEntityMetaDataFetch; import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; import static org.molgenis.data.postgresql.PostgreSqlQueryGenerator.*; -import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.*; +import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.getPersistedAttributesMref; +import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.getTableName; import static org.molgenis.data.support.EntityMetaDataUtils.*; public class PostgreSqlRepositoryCollection extends AbstractRepositoryCollection @@ -100,7 +102,7 @@ public Repository createRepository(EntityMetaData entityMeta) repository.setMetaData(entityMeta); if (!isTableExists(entityMeta)) { - create(entityMeta); + createTable(entityMeta); } return repository; } @@ -121,10 +123,9 @@ public Iterable getEntityNames() @Override public Repository getRepository(String name) { - EntityMetaData entityMetaData = dataService.query(ENTITY_META_DATA, EntityMetaData.class) - .eq(BACKEND, POSTGRESQL).and().eq(FULL_NAME, name).and().eq(ABSTRACT, false) - .fetch(getEntityMetaDataFetch()).findOne(); - return getRepository(entityMetaData); + EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(BACKEND, POSTGRESQL) + .and().eq(FULL_NAME, name).and().eq(ABSTRACT, false).fetch(getEntityMetaDataFetch()).findOne(); + return getRepository(entityMeta); } @Override @@ -183,6 +184,26 @@ public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attr) addAttributeRec(entityMeta, attr, true); } + @Override + public void updateAttribute(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (entityMeta.getAttribute(attr.getName()) == null) + { + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + } + updateAttributeRec(entityMeta, attr, updatedAttr); + } + + @Override + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (entityMeta.getAttribute(attr.getName()) == null) + { + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + } + deleteAttributeRec(entityMeta, attr); + } + /** * Recursively add attribute to entity and entities extending from this entity. * @@ -216,34 +237,13 @@ private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr, { createJunctionTable(entityMeta, attr); } - else if (attr.getDataType() != COMPOUND) + else { createColumn(entityMeta, attr); } - - if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) - { - createForeignKey(entityMeta, attr); - } - - String idAttrName = entityMeta.getIdAttribute().getName(); - if (attr.isUnique() && !attr.getName().equals(idAttrName)) - { - createUniqueKey(entityMeta, attr); - } } } - @Override - public void updateAttribute(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) - { - if (entityMeta.getAttribute(attr.getName()) == null) - { - throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); - } - updateAttributeRec(entityMeta, attr, updatedAttr); - } - /** * Recursively update attribute of entity and entities extending from this entity. * @@ -281,29 +281,94 @@ else if ((attr.getExpression() != null && updatedAttr.getExpression() == null) | } else { - // nullable changes - if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) - { - updateNillable(entityMeta, attr, updatedAttr); - } + updateColumn(entityMeta, attr, updatedAttr); + } + } + } - // unique changes - if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) - { - updateUnique(entityMeta, attr, updatedAttr); - } + /** + * Updates database column based on attribute changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateColumn(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + // nullable changes + if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + { + updateNillable(entityMeta, attr, updatedAttr); + } - // data type changes - if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) - { - updateDataType(entityMeta, attr, updatedAttr); - } + // unique changes + if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + { + updateUnique(entityMeta, attr, updatedAttr); + } + + // data type changes + if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) + { + updateDataType(entityMeta, attr, updatedAttr); + } + + // enum option changes + if (!Objects.equals(attr.getEnumOptions(), updatedAttr.getEnumOptions())) + { + updateEnumOptions(entityMeta, attr, updatedAttr); + } + } + + /** + * Updates check constraint based on enum value changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateEnumOptions(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (attr.getDataType() == ENUM) + { + if (updatedAttr.getDataType() == ENUM) + { + // update check constraint + dropCheckConstraint(entityMeta, attr); + createCheckConstraint(entityMeta, updatedAttr); + } + else + { + // drop check constraint + dropCheckConstraint(entityMeta, attr); + } + } + else + { + if (updatedAttr.getDataType() == ENUM) + { + createCheckConstraint(entityMeta, updatedAttr); } } } + /** + * Updates column data type and foreign key constraints based on data type update. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { + AttributeMetaData idAttr = entityMeta.getIdAttribute(); + if (idAttr != null && idAttr.getName().equals(attr.getName())) + { + throw new MolgenisDataException( + format("Data type of entity [%s] attribute [%s] cannot be modified, because [%s] is an ID attribute.", + entityMeta.getName(), attr.getName(), attr.getName())); + } + // do nothing on representation changes XREF --> CATEGORICAL if (isSingleReferenceType(attr) && isSingleReferenceType(updatedAttr)) { @@ -316,27 +381,10 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A return; } - AttributeMetaData idAttr = entityMeta.getIdAttribute(); - if (idAttr != null && idAttr.getName().equals(attr.getName())) - { - throw new MolgenisDataException( - format("Data type of entity [%s] attribute [%s] cannot be modified, because [%s] is an ID attribute.", - entityMeta.getName(), attr.getName(), attr.getName())); - } - // remove foreign key on data type updates such as XREF --> STRING if (isSingleReferenceType(attr) && !isReferenceType(updatedAttr)) { - String sqlDropForeignKey = PostgreSqlQueryGenerator.getSqlDropForeignKey(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Dropping foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlDropForeignKey); - } - } - jdbcTemplate.execute(sqlDropForeignKey); + dropForeignKey(entityMeta, attr); } String sqlSetDataType = getSqlSetDataType(entityMeta, updatedAttr); @@ -358,54 +406,30 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A } } - private void updateUnique(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + /** + * Updates unique constraint based on attribute unique changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateUnique(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { if (attr.isUnique() && !updatedAttr.isUnique()) { - AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + AttributeMetaData idAttr = entityMeta.getIdAttribute(); if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( - format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), - entityMetaData.getName())); + format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), entityMeta.getName())); } - String sqlDropUniqueKey = getSqlDropUniqueKey(entityMetaData, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Removing unique constraint for entity [{}] attribute [{}]", entityMetaData.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlDropUniqueKey); - } - } - jdbcTemplate.execute(sqlDropUniqueKey); + dropUniqueKey(entityMeta, updatedAttr); } else if (!attr.isUnique() && updatedAttr.isUnique()) { - String sqlCreateUniqueKey = getSqlCreateUniqueKey(entityMetaData, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating unique constraint for entity [{}] attribute [{}]", entityMetaData.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlCreateUniqueKey); - } - } - jdbcTemplate.execute(sqlCreateUniqueKey); - } - } - - @Override - public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) - { - if (entityMeta.getAttribute(attr.getName()) == null) - { - throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + createUniqueKey(entityMeta, updatedAttr); } - deleteAttributeRec(entityMeta, attr); } /** @@ -430,16 +454,7 @@ private void deleteAttributeRec(EntityMetaData entityMeta, AttributeMetaData att } else { - String dropColumnSql = getSqlDropColumn(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", dropColumnSql); - } - } - jdbcTemplate.execute(dropColumnSql); + dropColumn(entityMeta, attr); } } @@ -489,14 +504,14 @@ private boolean isTableExists(String tableName) } } - private void updateNillable(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + private void updateNillable(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { if (attr.isNillable() && !updatedAttr.isNillable()) { - String sqlSetNotNull = getSqlSetNotNull(entityMetaData, updatedAttr); + String sqlSetNotNull = getSqlSetNotNull(entityMeta, updatedAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating not null constraint for entity [{}] attribute [{}]", entityMetaData.getName(), + LOG.debug("Creating not null constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { @@ -507,18 +522,18 @@ private void updateNillable(EntityMetaData entityMetaData, AttributeMetaData att } else if (!attr.isNillable() && updatedAttr.isNillable()) { - AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + AttributeMetaData idAttr = entityMeta.getIdAttribute(); if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( format("ID attribute [%s] of entity [%s] cannot be nullable", attr.getName(), - entityMetaData.getName())); + entityMeta.getName())); } - String sqlDropNotNull = getSqlDropNotNull(entityMetaData, updatedAttr); + String sqlDropNotNull = getSqlDropNotNull(entityMeta, updatedAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Removing not null constraint for entity [{}] attribute [{}]", entityMetaData.getName(), + LOG.debug("Removing not null constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { @@ -530,8 +545,9 @@ else if (!attr.isNillable() && updatedAttr.isNillable()) } // @Transactional FIXME enable when bootstrapping transaction issue has been resolved - private void create(EntityMetaData entityMeta) + private void createTable(EntityMetaData entityMeta) { + // create table String createTableSql = getSqlCreateTable(entityMeta); if (LOG.isDebugEnabled()) { @@ -543,24 +559,8 @@ private void create(EntityMetaData entityMeta) } jdbcTemplate.execute(createTableSql); - String idAttrName = entityMeta.getIdAttribute().getName(); - getPersistedAttributes(entityMeta).forEach(attr -> - { - // add mref tables - if (isMultipleReferenceType(attr)) - { - createJunctionTable(entityMeta, attr); - } - else if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) - { - createForeignKey(entityMeta, attr); - } - - if (attr.isUnique() && !attr.getName().equals(idAttrName)) - { - createUniqueKey(entityMeta, attr); - } - }); + // create junction tables for attributes referencing multiple entities + getPersistedAttributesMref(entityMeta).forEach(attr -> createJunctionTable(entityMeta, attr)); } private void createForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) @@ -577,6 +577,20 @@ private void createForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(createForeignKeySql); } + private void dropForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropForeignKeySql = getSqlDropForeignKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropForeignKeySql); + } + } + jdbcTemplate.execute(dropForeignKeySql); + } + private void createUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { String createUniqueKeySql = getSqlCreateUniqueKey(entityMeta, attr); @@ -591,6 +605,48 @@ private void createUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(createUniqueKeySql); } + private void dropUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropUniqueKeySql = getSqlDropUniqueKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping unique key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropUniqueKeySql); + } + } + jdbcTemplate.execute(dropUniqueKeySql); + } + + private void createCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + String sqlCreateCheckConstraint = getSqlCreateCheckConstraint(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating check constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlCreateCheckConstraint); + } + } + jdbcTemplate.execute(sqlCreateCheckConstraint); + } + + private void dropCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + String sqlDropCheckConstraint = getSqlDropCheckConstraint(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping check constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlDropCheckConstraint); + } + } + jdbcTemplate.execute(sqlDropCheckConstraint); + } + private void createColumn(EntityMetaData entityMeta, AttributeMetaData attr) { String addColumnSql = getSqlAddColumn(entityMeta, attr); @@ -605,6 +661,20 @@ private void createColumn(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(addColumnSql); } + private void dropColumn(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropColumnSql = getSqlDropColumn(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropColumnSql); + } + } + jdbcTemplate.execute(dropColumnSql); + } + private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 93cb32ff454..80608f71389 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -410,6 +410,20 @@ public void addAttribute() verify(jdbcTemplate).execute("ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL"); } + @Test + public void addAttributeUnique() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + when(attr.isUnique()).thenReturn(true); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + verify(jdbcTemplate).execute( + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_key\" UNIQUE (\"attr\")"); + } + @Test public void addAttributeAbstractEntity() { From 640090e0677b5c7338235e4b13c116ddeb7b5643 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 15:01:59 +0200 Subject: [PATCH 14/49] Change allowed transitions to disallowed transitions --- .../postgresql/PostgreSqlQueryGenerator.java | 10 --- .../PostgreSqlQueryGeneratorTest.java | 24 ------ .../AttributeMetaDataRepositoryDecorator.java | 73 +++++++------------ 3 files changed, 27 insertions(+), 80 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index 7ebc5866f30..fa3cc1db35e 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -111,21 +111,11 @@ static String getSqlDropCheckConstraint(EntityMetaData entityMeta, AttributeMeta static String getSqlSetNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - if (!attr.isNillable()) - { - throw new MolgenisDataException( - format("Entity [%s] attribute [%s] is not nillable", entityMeta.getName(), attr.getName())); - } return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " SET NOT NULL"; } static String getSqlDropNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - if (attr.isNillable()) - { - throw new MolgenisDataException( - format("Entity [%s] attribute [%s] is nillable", entityMeta.getName(), attr.getName())); - } return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " DROP NOT NULL"; } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index c9c424b8904..1df08ca13a8 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -35,21 +35,6 @@ public void getSqlSetNotNull() "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET NOT NULL"); } - @Test(expectedExceptions = MolgenisDataException.class) - public void getSqlSetNotNullAttrNotNillable() - { - EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); - when(attr.isNillable()).thenReturn(false); - PostgreSqlQueryGenerator.getSqlSetNotNull(entityMeta, attr); - } - - @Test - public void getSqlSetDataType() - { - - } - @Test public void getSqlDropNotNull() { @@ -60,15 +45,6 @@ public void getSqlDropNotNull() "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" DROP NOT NULL"); } - @Test(expectedExceptions = MolgenisDataException.class) - public void getSqlDropNotNullAttrNotNillable() - { - EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); - AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); - when(attr.isNillable()).thenReturn(true); - PostgreSqlQueryGenerator.getSqlDropNotNull(entityMeta, attr); - } - @Test public void getSqlCreateTable() { diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java index aa3a1f4a7a9..b0e91a2c47e 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java @@ -359,63 +359,44 @@ private void validateUpdate(AttributeMetaData currentAttr, AttributeMetaData new } } - private static EnumMap> DATA_TYPE_ALLOWED_TRANSITIONS; + private static EnumMap> DATA_TYPE_DISALLOWED_TRANSITIONS; static { - DATA_TYPE_ALLOWED_TRANSITIONS = new EnumMap<>(AttributeType.class); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(BOOL, EnumSet.of(CATEGORICAL, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF - // not allowed : CATEGORICAL, COMPOUND, FILE - DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL, - EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL_MREF, EnumSet.of(MREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.noneOf(AttributeType.class)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL, DATE_TIME, HTML, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL, DATE, HTML, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(DECIMAL, EnumSet.of(CATEGORICAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(ENUM, EnumSet.noneOf(AttributeType.class)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(FILE, EnumSet.noneOf(AttributeType.class)); - // not implemented: HTML -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : HTML -> COMPOUND, FILE, HTML - DATA_TYPE_ALLOWED_TRANSITIONS.put(HTML, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(INT, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(LONG, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, INT, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(MREF, EnumSet.of(CATEGORICAL_MREF)); - // not implemented: SCRIPT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : SCRIPT -> COMPOUND, FILE, SCRIPT - DATA_TYPE_ALLOWED_TRANSITIONS.put(SCRIPT, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, STRING, TEXT, XREF)); - // not implemented: TEXT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : TEXT -> COMPOUND, FILE, TEXT - // not implemented: STRING -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : STRING -> COMPOUND, FILE, STRING - DATA_TYPE_ALLOWED_TRANSITIONS.put(STRING, - EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(TEXT, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, XREF)); - // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF - // not allowed : COMPOUND, FILE, XREF - DATA_TYPE_ALLOWED_TRANSITIONS.put(XREF, - EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT)); + // transitions to EMAIL and HYPERLINK not allowed because existing values not checked by PostgreSQL + // transitions to CATEGORICAL_MREF and MREF not allowed because junction tables updated not implemented + // transitions to FILE not allowed because associated file in FileStore not created/removed + DATA_TYPE_DISALLOWED_TRANSITIONS = new EnumMap<>(AttributeType.class); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(BOOL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(CATEGORICAL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(CATEGORICAL_MREF, EnumSet.complementOf(EnumSet.of(MREF))); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DECIMAL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(CATEGORICAL_MREF, MREF, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(ENUM, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(FILE, EnumSet.allOf(AttributeType.class)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(HTML, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(INT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(LONG, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(MREF, EnumSet.complementOf(EnumSet.of(CATEGORICAL_MREF))); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(SCRIPT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(STRING, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(TEXT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(XREF, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); } private static void validateUpdateDataType(AttributeType currentDataType, AttributeType newDataType) { - EnumSet allowedDataTypes = DATA_TYPE_ALLOWED_TRANSITIONS.get(currentDataType); - if (allowedDataTypes == null || !allowedDataTypes.contains(newDataType)) + EnumSet disallowedDataTypes = DATA_TYPE_DISALLOWED_TRANSITIONS.get(currentDataType); + if (disallowedDataTypes.contains(newDataType)) { throw new MolgenisDataException( format("Attribute data type update from [%s] to [%s] not allowed, allowed types are %s", currentDataType.toString(), newDataType.toString(), - allowedDataTypes != null ? allowedDataTypes.toString() : "[]")); + EnumSet.complementOf(disallowedDataTypes).toString())); } } From 730eddc4c8153d54244911206934481e47536643 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 16 Aug 2016 15:25:33 +0200 Subject: [PATCH 15/49] Replace incorrect import --- .../org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index fa3cc1db35e..c48b34c729f 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -1,6 +1,6 @@ package org.molgenis.data.postgresql; -import autovalue.shaded.com.google.common.common.collect.Lists; +import com.google.common.collect.Lists; import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.*; import org.molgenis.data.QueryRule.Operator; From fe7c82b6cf434c0f48a8bfeee76441e22acffab2 Mon Sep 17 00:00:00 2001 From: Dennis Hendriksen Date: Tue, 16 Aug 2016 19:10:48 +0200 Subject: [PATCH 16/49] Meta data editing enum, update allowed transitions (#5193) * Various small code improvements * ENH add getDropForeignKey for an entity attribute * Add updateAttribute for single-referencing attributes to/from non-referencing attributes Add updateAttribute unit tests Fix logging statements logging wrong entity name Fix updateAttribute for entitities without id attribute (e.g. abstract entities) * Add allowed data type transitions for single-reference attribute types * Fix attribute update of abstract entity * Handle to/from compound attribute type changes Handle to/from attribute expression changes Handle compound attributes consistently in PostgreSQL repository collection * ENH add enum options validation expression ENH add ref entity validation expression ENH allow user to update ref entity * ENH add allowed attribute data type updates transitions fro decimal/int/long to string ENH add allowed attribute data type updates transitions fro int/long to bool * Add 'and' operator for use in JavaScript Magma scripts * FIx ignore representational data type changes * Resolve IntelliJ warnings, reuse code, add tests * Add tests, reuse code, add documentation * Change allowed transitions to disallowed transitions * Replace incorrect import --- .../postgresql/PostgreSqlQueryGenerator.java | 243 ++++++++----- .../PostgreSqlRepositoryCollection.java | 336 +++++++++++------- .../PostgreSqlQueryGeneratorTest.java | 167 ++++++++- .../PostgreSqlRepositoryCollectionTest.java | 14 + .../AttributeMetaDataRepositoryDecorator.java | 73 ++-- 5 files changed, 559 insertions(+), 274 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index a33ba257b83..c48b34c729f 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -1,5 +1,6 @@ package org.molgenis.data.postgresql; +import com.google.common.collect.Lists; import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.*; import org.molgenis.data.QueryRule.Operator; @@ -8,7 +9,6 @@ import org.molgenis.data.support.QueryImpl; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -16,6 +16,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; import static java.util.stream.IntStream.range; import static java.util.stream.StreamSupport.stream; import static org.molgenis.MolgenisFieldTypes.AttributeType.BOOL; @@ -33,12 +34,16 @@ private PostgreSqlQueryGenerator() } - static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + private static String getSqlConstraintPrimaryKey(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder strBuilder = new StringBuilder(); - strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD CONSTRAINT ") - .append(getForeignKeyName(entityMeta, attr)).append(" FOREIGN KEY (").append(getColumnName(attr)) - .append(") REFERENCES ").append(getTableName(attr.getRefEntity())).append('(') + return "CONSTRAINT " + getPrimaryKeyName(entityMeta, attr) + " PRIMARY KEY (" + getColumnName(attr) + ')'; + } + + private static String getSqlForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + StringBuilder strBuilder = new StringBuilder("CONSTRAINT ").append(getForeignKeyName(entityMeta, attr)) + .append(" FOREIGN KEY (").append(getColumnName(attr)).append(") REFERENCES ") + .append(getTableName(attr.getRefEntity())).append('(') .append(getColumnName(attr.getRefEntity().getIdAttribute())).append(')'); // for self-referencing data defer checking constraints until the end of the transaction @@ -49,88 +54,115 @@ static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaDat return strBuilder.toString(); } + private static String getSqlUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "CONSTRAINT " + getUniqueKeyName(entityMeta, attr) + " UNIQUE (" + getColumnName(attr) + ')'; + } + + private static String getSqlCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (attr.getDataType() != ENUM) + { + throw new MolgenisDataException( + format("Check constraint only allowed for attribute type [%s]", ENUM.toString())); + } + + return "CONSTRAINT " + getCheckConstraintName(entityMeta, attr) + " CHECK (" + getColumnName(attr) + " IN (" + + attr.getEnumOptions().stream().map(enumOption -> '\'' + enumOption + '\'').collect(joining(",")) + + "))"; + } + + static String getSqlCreateForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlForeignKey(entityMeta, attr); + } + static String getSqlDropForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder strBuilder = new StringBuilder(); - strBuilder.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP CONSTRAINT ") - .append(getForeignKeyName(entityMeta, attr)); - return strBuilder.toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getForeignKeyName(entityMeta, attr); } static String getSqlCreateUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { - // PostgreSQL name convention - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD CONSTRAINT ") - .append(getUniqueKeyName(entityMeta, attr)).append(" UNIQUE (").append(getColumnName(attr)).append(')') - .toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlUniqueKey(entityMeta, attr); } static String getSqlDropUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { - // PostgreSQL name convention - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP CONSTRAINT ") - .append(getUniqueKeyName(entityMeta, attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getUniqueKeyName(entityMeta, attr); + } + + static String getSqlCreateCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + return "ALTER TABLE " + getTableName(entityMeta) + " ADD " + getSqlCheckConstraint(entityMeta, attr); + } + + static String getSqlDropCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (attr.getDataType() != ENUM) + { + throw new MolgenisDataException( + format("Check constraint only allowed for attribute type [%s]", ENUM.toString())); + } + + return "ALTER TABLE " + getTableName(entityMeta) + " DROP CONSTRAINT " + getCheckConstraintName(entityMeta, + attr); } static String getSqlSetNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" SET NOT NULL").toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " SET NOT NULL"; } static String getSqlDropNotNull(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" DROP NOT NULL").toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " DROP NOT NULL"; } static String getSqlSetDataType(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ALTER COLUMN ") - .append(getColumnName(attr)).append(" SET DATA TYPE ").append(getPostgreSqlType(attr)).append(" USING ") - .append(getColumnName(attr)).append("::").append(getPostgreSqlType(attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " ALTER COLUMN " + getColumnName(attr) + " SET DATA TYPE " + + getPostgreSqlType(attr) + " USING " + getColumnName(attr) + "::" + getPostgreSqlType(attr); } static String getSqlAddColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - StringBuilder sql = new StringBuilder(); - sql.append("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD "); - getSqlAttribute(sql, attr); + StringBuilder sql = new StringBuilder("ALTER TABLE ").append(getTableName(entityMeta)).append(" ADD "); + sql.append(getSqlColumn(entityMeta, attr)); + List sqlTableConstraints = getSqlTableConstraints(entityMeta, attr); + if (!sqlTableConstraints.isEmpty()) + { + sqlTableConstraints.forEach(sqlTableConstraint -> sql.append(",ADD ").append(sqlTableConstraint)); + } return sql.toString(); } static String getSqlCreateTable(EntityMetaData entityMeta) { - StringBuilder sql = new StringBuilder(); - sql.append("CREATE TABLE ").append(getTableName(entityMeta)).append('('); - - getPersistedAttributesNonMref(entityMeta).forEach(attr -> - { - getSqlAttribute(sql, attr); - sql.append(", "); - }); - - // primary key is first attribute unless otherwise indicated - AttributeMetaData idAttribute = entityMeta.getIdAttribute(); + List persistedNonMrefAttrs = getPersistedAttributesNonMref(entityMeta).collect(toList()); - if (idAttribute == null) - { - throw new MolgenisDataException(format("Missing idAttribute for entity [%s]", entityMeta.getName())); - } + StringBuilder sql = new StringBuilder("CREATE TABLE ").append(getTableName(entityMeta)).append('('); - if (isReferenceType(idAttribute)) + // add columns + for (Iterator it = persistedNonMrefAttrs.iterator(); it.hasNext(); ) { - throw new RuntimeException(format("primary key(%s.%s) cannot be XREF or MREF", getTableName(entityMeta), - getColumnName(idAttribute))); + sql.append(getSqlColumn(entityMeta, it.next())); + if (it.hasNext()) + { + sql.append(','); + } } - if (idAttribute.isNillable()) + // add table constraints + for (AttributeMetaData persistedNonMrefAttr : persistedNonMrefAttrs) { - throw new RuntimeException(format("idAttribute (%s.%s) should not be nillable", getTableName(entityMeta), - getColumnName(idAttribute))); + List sqlTableConstraints = getSqlTableConstraints(entityMeta, persistedNonMrefAttr); + if (!sqlTableConstraints.isEmpty()) + { + sqlTableConstraints.forEach(sqlTableConstraint -> sql.append(',').append(sqlTableConstraint)); + } } - sql.append("PRIMARY KEY (").append(getColumnName(entityMeta.getIdAttribute())).append(')'); sql.append(')'); return sql.toString(); @@ -139,9 +171,8 @@ static String getSqlCreateTable(EntityMetaData entityMeta) static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { AttributeMetaData idAttr = entityMeta.getIdAttribute(); - StringBuilder sql = new StringBuilder(); - - sql.append("CREATE TABLE IF NOT EXISTS ").append(getJunctionTableName(entityMeta, attr)).append(" (") + StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS ") + .append(getJunctionTableName(entityMeta, attr)).append(" (") .append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(" INT,").append(getColumnName(idAttr)) .append(' ').append(getPostgreSqlType(idAttr)).append(" NOT NULL, ").append(getColumnName(attr)) .append(' ').append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())) @@ -197,14 +228,12 @@ static String getSqlDropTable(EntityMetaData entityMeta) static String getSqlDropColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("ALTER TABLE ").append(getTableName(entityMeta)).append(" DROP COLUMN ") - .append(getColumnName(attr)).toString(); + return "ALTER TABLE " + getTableName(entityMeta) + " DROP COLUMN " + getColumnName(attr); } static String getSqlInsert(EntityMetaData entityMeta) { - StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO ").append(getTableName(entityMeta)).append(" ("); + StringBuilder sql = new StringBuilder("INSERT INTO ").append(getTableName(entityMeta)).append(" ("); StringBuilder params = new StringBuilder(); getPersistedAttributesNonMref(entityMeta).forEach(attr -> { @@ -222,14 +251,14 @@ static String getSqlInsert(EntityMetaData entityMeta) static String getSqlInsertMref(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData idAttr) { - return new StringBuilder().append("INSERT INTO ").append(getJunctionTableName(entityMeta, attr)).append(" (") - .append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(',').append(getColumnName(idAttr)) - .append(',').append(getColumnName(attr)).append(") VALUES (?,?,?)").toString(); + return "INSERT INTO " + getJunctionTableName(entityMeta, attr) + " (" + getColumnName( + JUNCTION_TABLE_ORDER_ATTR_NAME) + ',' + getColumnName(idAttr) + ',' + getColumnName(attr) + + ") VALUES (?,?,?)"; } static String getSqlDeleteAll(EntityMetaData entityMeta) { - return new StringBuilder().append("DELETE FROM ").append(getTableName(entityMeta)).toString(); + return "DELETE FROM " + getTableName(entityMeta); } static String getSqlDelete(EntityMetaData entityMeta) @@ -239,8 +268,7 @@ static String getSqlDelete(EntityMetaData entityMeta) static String getSqlDelete(String tableName, AttributeMetaData attr) { - return new StringBuilder().append("DELETE FROM ").append(tableName).append(" WHERE ") - .append(getColumnName(attr)).append(" = ?").toString(); + return "DELETE FROM " + tableName + " WHERE " + getColumnName(attr) + " = ?"; } static String getJunctionTableSelect(EntityMetaData entityMeta, AttributeMetaData attr, int numOfIds) @@ -371,16 +399,16 @@ static String getSqlCount(EntityMetaData entityMeta, Query sqlBuilder.append("(*)"); } - String from = getSqlFromForCount(entityMeta, q, mrefAttrsInQuery); + String from = getSqlFromForCount(entityMeta, mrefAttrsInQuery); String where = getSqlWhere(entityMeta, q, parameters, 0); sqlBuilder.append(from).append(" WHERE ").append(where); } return sqlBuilder.toString(); } - private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) + private static String getSqlColumn(EntityMetaData entityMeta, AttributeMetaData attr) { - sql.append(getColumnName(attr)).append(' '); + StringBuilder sqlBuilder = new StringBuilder(getColumnName(attr)).append(' '); AttributeType attrType = attr.getDataType(); switch (attrType) @@ -398,12 +426,12 @@ private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) case SCRIPT: case STRING: case TEXT: - sql.append(getPostgreSqlType(attr)); + sqlBuilder.append(getPostgreSqlType(attr)); break; case CATEGORICAL: case FILE: case XREF: - sql.append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())); + sqlBuilder.append(getPostgreSqlType(attr.getRefEntity().getIdAttribute())); break; case COMPOUND: case CATEGORICAL_MREF: @@ -413,22 +441,57 @@ private static void getSqlAttribute(StringBuilder sql, AttributeMetaData attr) throw new RuntimeException(format("Unknown attribute type [%s]", attrType.toString())); } - if (!attr.isNillable()) + String sqlColumnConstraints = getSqlColumnConstraints(entityMeta, attr); + if (!sqlColumnConstraints.isEmpty()) { - sql.append(" NOT NULL"); + sqlBuilder.append(' ').append(sqlColumnConstraints); } + return sqlBuilder.toString(); + } - if (attr.getDataType() == ENUM) + private static String getSqlColumnConstraints(EntityMetaData entityMeta, AttributeMetaData attr) + { + StringBuilder sqlBuilder = new StringBuilder(); + if (!attr.getName().equals(entityMeta.getIdAttribute().getName())) { - sql.append(" CHECK (").append(getColumnName(attr)).append(" IN (") - .append(attr.getEnumOptions().stream().map(enumOption -> '\'' + enumOption + '\'') - .collect(joining(","))).append("))"); + if (!attr.isNillable()) + { + sqlBuilder.append("NOT NULL"); + } } + return sqlBuilder.toString(); + } + + private static List getSqlTableConstraints(EntityMetaData entityMeta, AttributeMetaData attr) + { + List tableConstraints = Lists.newArrayList(); + + if (attr.getName().equals(entityMeta.getIdAttribute().getName())) + { + tableConstraints.add(getSqlConstraintPrimaryKey(entityMeta, attr)); + } + else + { + if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) + { + tableConstraints.add(getSqlForeignKey(entityMeta, attr)); + } + if (attr.isUnique()) + { + tableConstraints.add(getSqlUniqueKey(entityMeta, attr)); + } + if (attr.getDataType() == ENUM) + { + tableConstraints.add(getSqlCheckConstraint(entityMeta, attr)); + } + } + + return tableConstraints; } private static String getSqlDropTable(String tableName) { - return new StringBuilder("DROP TABLE ").append(tableName).toString(); + return "DROP TABLE " + tableName; } private static String getSqlWhere(EntityMetaData entityMeta, Query q, List parameters, @@ -696,7 +759,7 @@ private static String getSqlSort(EntityMetaData entityMeta, Q private static String getSqlFrom(EntityMetaData entityMeta, Query q) { - StringBuilder from = new StringBuilder().append(" FROM ").append(getTableName(entityMeta)).append(" AS this"); + StringBuilder from = new StringBuilder(" FROM ").append(getTableName(entityMeta)).append(" AS this"); AttributeMetaData idAttribute = entityMeta.getIdAttribute(); @@ -715,10 +778,10 @@ private static String getSqlFrom(EntityMetaData entityMeta, Q return from.toString(); } - private static String getSqlFromForCount(EntityMetaData entityMeta, Query q, + private static String getSqlFromForCount(EntityMetaData entityMeta, List mrefAttrsInQuery) { - StringBuilder from = new StringBuilder().append(" FROM ").append(getTableName(entityMeta)).append(" AS this"); + StringBuilder from = new StringBuilder(" FROM ").append(getTableName(entityMeta)).append(" AS this"); AttributeMetaData idAttribute = entityMeta.getIdAttribute(); @@ -743,34 +806,42 @@ private static String getColumnName(AttributeMetaData attr) private static String getColumnName(String attrName) { - return new StringBuilder().append('"').append(attrName).append('"').toString(); + return "\"" + attrName + '"'; } private static String getFilterColumnName(AttributeMetaData attr, int filterIndex) { - return new StringBuilder().append('"').append(attr.getName()).append("_filter").append(filterIndex).append('"') - .toString(); + return "\"" + attr.getName() + "_filter" + filterIndex + '"'; + } + + private static String getPrimaryKeyName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getConstraintName(entityMeta, attr, "pkey"); } private static String getForeignKeyName(EntityMetaData entityMeta, AttributeMetaData attr) { - return getKeyName(entityMeta, attr, "fkey"); + return getConstraintName(entityMeta, attr, "fkey"); } private static String getUniqueKeyName(EntityMetaData entityMeta, AttributeMetaData attr) { - return getKeyName(entityMeta, attr, "key"); + return getConstraintName(entityMeta, attr, "key"); + } + + private static String getCheckConstraintName(EntityMetaData entityMeta, AttributeMetaData attr) + { + return getConstraintName(entityMeta, attr, "chk"); } - private static String getKeyName(EntityMetaData entityMeta, AttributeMetaData attr, String keyPostfix) + private static String getConstraintName(EntityMetaData entityMeta, AttributeMetaData attr, String constraintPostfix) { - return new StringBuilder().append('"').append(entityMeta.getName()).append('_').append(attr.getName()) - .append('_').append(keyPostfix).append('"').toString(); + return "\"" + entityMeta.getName() + '_' + attr.getName() + '_' + constraintPostfix + '"'; } private static List getMrefQueryAttrs(EntityMetaData entityMeta, Query q) { - List mrefAttrsInQuery = new ArrayList<>(); + List mrefAttrsInQuery = Lists.newArrayList(); getMrefQueryFieldsRec(entityMeta, q.getRules(), mrefAttrsInQuery); return mrefAttrsInQuery; } diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index d997df4e141..2ffffe3868c 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -23,12 +23,14 @@ import static java.util.EnumSet.of; import static java.util.Objects.requireNonNull; import static org.molgenis.MolgenisFieldTypes.AttributeType.COMPOUND; +import static org.molgenis.MolgenisFieldTypes.AttributeType.ENUM; import static org.molgenis.data.RepositoryCollectionCapability.*; import static org.molgenis.data.i18n.model.LanguageMetaData.*; import static org.molgenis.data.meta.MetaUtils.getEntityMetaDataFetch; import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; import static org.molgenis.data.postgresql.PostgreSqlQueryGenerator.*; -import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.*; +import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.getPersistedAttributesMref; +import static org.molgenis.data.postgresql.PostgreSqlQueryUtils.getTableName; import static org.molgenis.data.support.EntityMetaDataUtils.*; public class PostgreSqlRepositoryCollection extends AbstractRepositoryCollection @@ -100,7 +102,7 @@ public Repository createRepository(EntityMetaData entityMeta) repository.setMetaData(entityMeta); if (!isTableExists(entityMeta)) { - create(entityMeta); + createTable(entityMeta); } return repository; } @@ -121,10 +123,9 @@ public Iterable getEntityNames() @Override public Repository getRepository(String name) { - EntityMetaData entityMetaData = dataService.query(ENTITY_META_DATA, EntityMetaData.class) - .eq(BACKEND, POSTGRESQL).and().eq(FULL_NAME, name).and().eq(ABSTRACT, false) - .fetch(getEntityMetaDataFetch()).findOne(); - return getRepository(entityMetaData); + EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(BACKEND, POSTGRESQL) + .and().eq(FULL_NAME, name).and().eq(ABSTRACT, false).fetch(getEntityMetaDataFetch()).findOne(); + return getRepository(entityMeta); } @Override @@ -183,6 +184,26 @@ public void addAttribute(EntityMetaData entityMeta, AttributeMetaData attr) addAttributeRec(entityMeta, attr, true); } + @Override + public void updateAttribute(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (entityMeta.getAttribute(attr.getName()) == null) + { + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + } + updateAttributeRec(entityMeta, attr, updatedAttr); + } + + @Override + public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) + { + if (entityMeta.getAttribute(attr.getName()) == null) + { + throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + } + deleteAttributeRec(entityMeta, attr); + } + /** * Recursively add attribute to entity and entities extending from this entity. * @@ -216,34 +237,13 @@ private void addAttributeRec(EntityMetaData entityMeta, AttributeMetaData attr, { createJunctionTable(entityMeta, attr); } - else if (attr.getDataType() != COMPOUND) + else { createColumn(entityMeta, attr); } - - if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) - { - createForeignKey(entityMeta, attr); - } - - String idAttrName = entityMeta.getIdAttribute().getName(); - if (attr.isUnique() && !attr.getName().equals(idAttrName)) - { - createUniqueKey(entityMeta, attr); - } } } - @Override - public void updateAttribute(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) - { - if (entityMeta.getAttribute(attr.getName()) == null) - { - throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); - } - updateAttributeRec(entityMeta, attr, updatedAttr); - } - /** * Recursively update attribute of entity and entities extending from this entity. * @@ -281,29 +281,94 @@ else if ((attr.getExpression() != null && updatedAttr.getExpression() == null) | } else { - // nullable changes - if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) - { - updateNillable(entityMeta, attr, updatedAttr); - } + updateColumn(entityMeta, attr, updatedAttr); + } + } + } - // unique changes - if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) - { - updateUnique(entityMeta, attr, updatedAttr); - } + /** + * Updates database column based on attribute changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateColumn(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + // nullable changes + if (!Objects.equals(attr.isNillable(), updatedAttr.isNillable())) + { + updateNillable(entityMeta, attr, updatedAttr); + } - // data type changes - if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) - { - updateDataType(entityMeta, attr, updatedAttr); - } + // unique changes + if (!Objects.equals(attr.isUnique(), updatedAttr.isUnique())) + { + updateUnique(entityMeta, attr, updatedAttr); + } + + // data type changes + if (!Objects.equals(attr.getDataType(), updatedAttr.getDataType())) + { + updateDataType(entityMeta, attr, updatedAttr); + } + + // enum option changes + if (!Objects.equals(attr.getEnumOptions(), updatedAttr.getEnumOptions())) + { + updateEnumOptions(entityMeta, attr, updatedAttr); + } + } + + /** + * Updates check constraint based on enum value changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateEnumOptions(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (attr.getDataType() == ENUM) + { + if (updatedAttr.getDataType() == ENUM) + { + // update check constraint + dropCheckConstraint(entityMeta, attr); + createCheckConstraint(entityMeta, updatedAttr); + } + else + { + // drop check constraint + dropCheckConstraint(entityMeta, attr); + } + } + else + { + if (updatedAttr.getDataType() == ENUM) + { + createCheckConstraint(entityMeta, updatedAttr); } } } + /** + * Updates column data type and foreign key constraints based on data type update. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { + AttributeMetaData idAttr = entityMeta.getIdAttribute(); + if (idAttr != null && idAttr.getName().equals(attr.getName())) + { + throw new MolgenisDataException( + format("Data type of entity [%s] attribute [%s] cannot be modified, because [%s] is an ID attribute.", + entityMeta.getName(), attr.getName(), attr.getName())); + } + // do nothing on representation changes XREF --> CATEGORICAL if (isSingleReferenceType(attr) && isSingleReferenceType(updatedAttr)) { @@ -316,27 +381,10 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A return; } - AttributeMetaData idAttr = entityMeta.getIdAttribute(); - if (idAttr != null && idAttr.getName().equals(attr.getName())) - { - throw new MolgenisDataException( - format("Data type of entity [%s] attribute [%s] cannot be modified, because [%s] is an ID attribute.", - entityMeta.getName(), attr.getName(), attr.getName())); - } - // remove foreign key on data type updates such as XREF --> STRING if (isSingleReferenceType(attr) && !isReferenceType(updatedAttr)) { - String sqlDropForeignKey = PostgreSqlQueryGenerator.getSqlDropForeignKey(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Dropping foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlDropForeignKey); - } - } - jdbcTemplate.execute(sqlDropForeignKey); + dropForeignKey(entityMeta, attr); } String sqlSetDataType = getSqlSetDataType(entityMeta, updatedAttr); @@ -358,54 +406,30 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A } } - private void updateUnique(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + /** + * Updates unique constraint based on attribute unique changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateUnique(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { if (attr.isUnique() && !updatedAttr.isUnique()) { - AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + AttributeMetaData idAttr = entityMeta.getIdAttribute(); if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( - format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), - entityMetaData.getName())); + format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), entityMeta.getName())); } - String sqlDropUniqueKey = getSqlDropUniqueKey(entityMetaData, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Removing unique constraint for entity [{}] attribute [{}]", entityMetaData.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlDropUniqueKey); - } - } - jdbcTemplate.execute(sqlDropUniqueKey); + dropUniqueKey(entityMeta, updatedAttr); } else if (!attr.isUnique() && updatedAttr.isUnique()) { - String sqlCreateUniqueKey = getSqlCreateUniqueKey(entityMetaData, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Creating unique constraint for entity [{}] attribute [{}]", entityMetaData.getName(), - attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlCreateUniqueKey); - } - } - jdbcTemplate.execute(sqlCreateUniqueKey); - } - } - - @Override - public void deleteAttribute(EntityMetaData entityMeta, AttributeMetaData attr) - { - if (entityMeta.getAttribute(attr.getName()) == null) - { - throw new UnknownAttributeException(format("Unknown attribute [%s]", attr.getName())); + createUniqueKey(entityMeta, updatedAttr); } - deleteAttributeRec(entityMeta, attr); } /** @@ -430,16 +454,7 @@ private void deleteAttributeRec(EntityMetaData entityMeta, AttributeMetaData att } else { - String dropColumnSql = getSqlDropColumn(entityMeta, attr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", dropColumnSql); - } - } - jdbcTemplate.execute(dropColumnSql); + dropColumn(entityMeta, attr); } } @@ -489,14 +504,14 @@ private boolean isTableExists(String tableName) } } - private void updateNillable(EntityMetaData entityMetaData, AttributeMetaData attr, AttributeMetaData updatedAttr) + private void updateNillable(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) { if (attr.isNillable() && !updatedAttr.isNillable()) { - String sqlSetNotNull = getSqlSetNotNull(entityMetaData, updatedAttr); + String sqlSetNotNull = getSqlSetNotNull(entityMeta, updatedAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Creating not null constraint for entity [{}] attribute [{}]", entityMetaData.getName(), + LOG.debug("Creating not null constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { @@ -507,18 +522,18 @@ private void updateNillable(EntityMetaData entityMetaData, AttributeMetaData att } else if (!attr.isNillable() && updatedAttr.isNillable()) { - AttributeMetaData idAttr = entityMetaData.getIdAttribute(); + AttributeMetaData idAttr = entityMeta.getIdAttribute(); if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( format("ID attribute [%s] of entity [%s] cannot be nullable", attr.getName(), - entityMetaData.getName())); + entityMeta.getName())); } - String sqlDropNotNull = getSqlDropNotNull(entityMetaData, updatedAttr); + String sqlDropNotNull = getSqlDropNotNull(entityMeta, updatedAttr); if (LOG.isDebugEnabled()) { - LOG.debug("Removing not null constraint for entity [{}] attribute [{}]", entityMetaData.getName(), + LOG.debug("Removing not null constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); if (LOG.isTraceEnabled()) { @@ -530,8 +545,9 @@ else if (!attr.isNillable() && updatedAttr.isNillable()) } // @Transactional FIXME enable when bootstrapping transaction issue has been resolved - private void create(EntityMetaData entityMeta) + private void createTable(EntityMetaData entityMeta) { + // create table String createTableSql = getSqlCreateTable(entityMeta); if (LOG.isDebugEnabled()) { @@ -543,24 +559,8 @@ private void create(EntityMetaData entityMeta) } jdbcTemplate.execute(createTableSql); - String idAttrName = entityMeta.getIdAttribute().getName(); - getPersistedAttributes(entityMeta).forEach(attr -> - { - // add mref tables - if (isMultipleReferenceType(attr)) - { - createJunctionTable(entityMeta, attr); - } - else if (isSingleReferenceType(attr) && isPersistedInPostgreSql(attr.getRefEntity())) - { - createForeignKey(entityMeta, attr); - } - - if (attr.isUnique() && !attr.getName().equals(idAttrName)) - { - createUniqueKey(entityMeta, attr); - } - }); + // create junction tables for attributes referencing multiple entities + getPersistedAttributesMref(entityMeta).forEach(attr -> createJunctionTable(entityMeta, attr)); } private void createForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) @@ -577,6 +577,20 @@ private void createForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(createForeignKeySql); } + private void dropForeignKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropForeignKeySql = getSqlDropForeignKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping foreign key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropForeignKeySql); + } + } + jdbcTemplate.execute(dropForeignKeySql); + } + private void createUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) { String createUniqueKeySql = getSqlCreateUniqueKey(entityMeta, attr); @@ -591,6 +605,48 @@ private void createUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(createUniqueKeySql); } + private void dropUniqueKey(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropUniqueKeySql = getSqlDropUniqueKey(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping unique key for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropUniqueKeySql); + } + } + jdbcTemplate.execute(dropUniqueKeySql); + } + + private void createCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + String sqlCreateCheckConstraint = getSqlCreateCheckConstraint(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating check constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlCreateCheckConstraint); + } + } + jdbcTemplate.execute(sqlCreateCheckConstraint); + } + + private void dropCheckConstraint(EntityMetaData entityMeta, AttributeMetaData attr) + { + String sqlDropCheckConstraint = getSqlDropCheckConstraint(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping check constraint for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlDropCheckConstraint); + } + } + jdbcTemplate.execute(sqlDropCheckConstraint); + } + private void createColumn(EntityMetaData entityMeta, AttributeMetaData attr) { String addColumnSql = getSqlAddColumn(entityMeta, attr); @@ -605,6 +661,20 @@ private void createColumn(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(addColumnSql); } + private void dropColumn(EntityMetaData entityMeta, AttributeMetaData attr) + { + String dropColumnSql = getSqlDropColumn(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping column for entity [{}] attribute [{}]", entityMeta.getName(), attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", dropColumnSql); + } + } + jdbcTemplate.execute(dropColumnSql); + } + private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index da292c47261..1df08ca13a8 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -2,6 +2,7 @@ import org.molgenis.MolgenisFieldTypes.AttributeType; import org.molgenis.data.Entity; +import org.molgenis.data.MolgenisDataException; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.data.meta.model.Package; @@ -24,6 +25,110 @@ public class PostgreSqlQueryGeneratorTest { + @Test + public void getSqlSetNotNull() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(true); + assertEquals(PostgreSqlQueryGenerator.getSqlSetNotNull(entityMeta, attr), + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET NOT NULL"); + } + + @Test + public void getSqlDropNotNull() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.isNillable()).thenReturn(false); + assertEquals(PostgreSqlQueryGenerator.getSqlDropNotNull(entityMeta, attr), + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" DROP NOT NULL"); + } + + @Test + public void getSqlCreateTable() + { + // ref entity with string id attribute + AttributeMetaData refIdAttrStr = mock(AttributeMetaData.class); + when(refIdAttrStr.getName()).thenReturn("refIdAttrStr"); + when(refIdAttrStr.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMetaString = mock(EntityMetaData.class); + when(refEntityMetaString.getName()).thenReturn("refEntityStr"); + when(refEntityMetaString.getIdAttribute()).thenReturn(refIdAttrStr); + + // ref entity with int id attribute + AttributeMetaData refIdAttrInt = mock(AttributeMetaData.class); + when(refIdAttrInt.getName()).thenReturn("refIdAttrInt"); + when(refIdAttrInt.getDataType()).thenReturn(INT); + EntityMetaData refEntityMetaInt = mock(EntityMetaData.class); + when(refEntityMetaInt.getName()).thenReturn("refEntityInt"); + when(refEntityMetaInt.getIdAttribute()).thenReturn(refIdAttrInt); + + // entity with attributes of all types and flavors + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(idAttr.getDataType()).thenReturn(STRING); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + + List atomicAttrs = Lists.newArrayList(); + atomicAttrs.add(idAttr); + StringBuilder attrNameBuilder = new StringBuilder(16); + for (boolean hasExpression : newArrayList(false, true)) + { + for (boolean unique : newArrayList(false, true)) + { + for (boolean nillable : newArrayList(false, true)) + { + for (AttributeType attrType : AttributeType.values()) + { + if (attrType != COMPOUND) + { + attrNameBuilder.setLength(0); + attrNameBuilder.append(attrType.toString().toLowerCase()); + if (hasExpression) + { + attrNameBuilder.append("_expression"); + } + if (unique) + { + attrNameBuilder.append("_unique"); + } + if (nillable) + { + attrNameBuilder.append("_nillable"); + } + + AttributeMetaData attr = mock(AttributeMetaData.class); + when(attr.getName()).thenReturn(attrNameBuilder.toString()); + when(attr.getDataType()).thenReturn(attrType); + when(attr.getExpression()).thenReturn(hasExpression ? "expression" : null); + when(attr.isUnique()).thenReturn(unique); + when(attr.isNillable()).thenReturn(nillable); + + if (attrType == CATEGORICAL || attrType == CATEGORICAL_MREF) + { + when(attr.getRefEntity()).thenReturn(refEntityMetaString); + } + else if (attrType == FILE || attrType == XREF || attrType == MREF) + { + when(attr.getRefEntity()).thenReturn(refEntityMetaInt); + } + else if (attrType == ENUM) + { + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1")); + } + atomicAttrs.add(attr); + } + } + } + } + } + when(entityMeta.getAtomicAttributes()).thenReturn(atomicAttrs); + + assertEquals(PostgreSqlQueryGenerator.getSqlCreateTable(entityMeta), + "CREATE TABLE \"entity\"(\"id\" character varying(255),\"bool\" boolean NOT NULL,\"categorical\" character varying(255) NOT NULL,\"date\" date NOT NULL,\"date_time\" timestamp NOT NULL,\"decimal\" double precision NOT NULL,\"email\" character varying(255) NOT NULL,\"enum\" character varying(255) NOT NULL,\"file\" integer NOT NULL,\"html\" text NOT NULL,\"hyperlink\" character varying(255) NOT NULL,\"int\" integer NOT NULL,\"long\" bigint NOT NULL,\"script\" text NOT NULL,\"string\" character varying(255) NOT NULL,\"text\" text NOT NULL,\"xref\" integer NOT NULL,\"bool_nillable\" boolean,\"categorical_nillable\" character varying(255),\"date_nillable\" date,\"date_time_nillable\" timestamp,\"decimal_nillable\" double precision,\"email_nillable\" character varying(255),\"enum_nillable\" character varying(255),\"file_nillable\" integer,\"html_nillable\" text,\"hyperlink_nillable\" character varying(255),\"int_nillable\" integer,\"long_nillable\" bigint,\"script_nillable\" text,\"string_nillable\" character varying(255),\"text_nillable\" text,\"xref_nillable\" integer,\"bool_unique\" boolean NOT NULL,\"categorical_unique\" character varying(255) NOT NULL,\"date_unique\" date NOT NULL,\"date_time_unique\" timestamp NOT NULL,\"decimal_unique\" double precision NOT NULL,\"email_unique\" character varying(255) NOT NULL,\"enum_unique\" character varying(255) NOT NULL,\"file_unique\" integer NOT NULL,\"html_unique\" text NOT NULL,\"hyperlink_unique\" character varying(255) NOT NULL,\"int_unique\" integer NOT NULL,\"long_unique\" bigint NOT NULL,\"script_unique\" text NOT NULL,\"string_unique\" character varying(255) NOT NULL,\"text_unique\" text NOT NULL,\"xref_unique\" integer NOT NULL,\"bool_unique_nillable\" boolean,\"categorical_unique_nillable\" character varying(255),\"date_unique_nillable\" date,\"date_time_unique_nillable\" timestamp,\"decimal_unique_nillable\" double precision,\"email_unique_nillable\" character varying(255),\"enum_unique_nillable\" character varying(255),\"file_unique_nillable\" integer,\"html_unique_nillable\" text,\"hyperlink_unique_nillable\" character varying(255),\"int_unique_nillable\" integer,\"long_unique_nillable\" bigint,\"script_unique_nillable\" text,\"string_unique_nillable\" character varying(255),\"text_unique_nillable\" text,\"xref_unique_nillable\" integer,CONSTRAINT \"entity_id_pkey\" PRIMARY KEY (\"id\"),CONSTRAINT \"entity_categorical_fkey\" FOREIGN KEY (\"categorical\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_enum_chk\" CHECK (\"enum\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_fkey\" FOREIGN KEY (\"file\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_fkey\" FOREIGN KEY (\"xref\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_categorical_nillable_fkey\" FOREIGN KEY (\"categorical_nillable\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_enum_nillable_chk\" CHECK (\"enum_nillable\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_nillable_fkey\" FOREIGN KEY (\"file_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_nillable_fkey\" FOREIGN KEY (\"xref_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_bool_unique_key\" UNIQUE (\"bool_unique\"),CONSTRAINT \"entity_categorical_unique_fkey\" FOREIGN KEY (\"categorical_unique\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_categorical_unique_key\" UNIQUE (\"categorical_unique\"),CONSTRAINT \"entity_date_unique_key\" UNIQUE (\"date_unique\"),CONSTRAINT \"entity_date_time_unique_key\" UNIQUE (\"date_time_unique\"),CONSTRAINT \"entity_decimal_unique_key\" UNIQUE (\"decimal_unique\"),CONSTRAINT \"entity_email_unique_key\" UNIQUE (\"email_unique\"),CONSTRAINT \"entity_enum_unique_key\" UNIQUE (\"enum_unique\"),CONSTRAINT \"entity_enum_unique_chk\" CHECK (\"enum_unique\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_unique_fkey\" FOREIGN KEY (\"file_unique\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_file_unique_key\" UNIQUE (\"file_unique\"),CONSTRAINT \"entity_html_unique_key\" UNIQUE (\"html_unique\"),CONSTRAINT \"entity_hyperlink_unique_key\" UNIQUE (\"hyperlink_unique\"),CONSTRAINT \"entity_int_unique_key\" UNIQUE (\"int_unique\"),CONSTRAINT \"entity_long_unique_key\" UNIQUE (\"long_unique\"),CONSTRAINT \"entity_script_unique_key\" UNIQUE (\"script_unique\"),CONSTRAINT \"entity_string_unique_key\" UNIQUE (\"string_unique\"),CONSTRAINT \"entity_text_unique_key\" UNIQUE (\"text_unique\"),CONSTRAINT \"entity_xref_unique_fkey\" FOREIGN KEY (\"xref_unique\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_unique_key\" UNIQUE (\"xref_unique\"),CONSTRAINT \"entity_bool_unique_nillable_key\" UNIQUE (\"bool_unique_nillable\"),CONSTRAINT \"entity_categorical_unique_nillable_fkey\" FOREIGN KEY (\"categorical_unique_nillable\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\"),CONSTRAINT \"entity_categorical_unique_nillable_key\" UNIQUE (\"categorical_unique_nillable\"),CONSTRAINT \"entity_date_unique_nillable_key\" UNIQUE (\"date_unique_nillable\"),CONSTRAINT \"entity_date_time_unique_nillable_key\" UNIQUE (\"date_time_unique_nillable\"),CONSTRAINT \"entity_decimal_unique_nillable_key\" UNIQUE (\"decimal_unique_nillable\"),CONSTRAINT \"entity_email_unique_nillable_key\" UNIQUE (\"email_unique_nillable\"),CONSTRAINT \"entity_enum_unique_nillable_key\" UNIQUE (\"enum_unique_nillable\"),CONSTRAINT \"entity_enum_unique_nillable_chk\" CHECK (\"enum_unique_nillable\" IN ('enum0','enum1')),CONSTRAINT \"entity_file_unique_nillable_fkey\" FOREIGN KEY (\"file_unique_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_file_unique_nillable_key\" UNIQUE (\"file_unique_nillable\"),CONSTRAINT \"entity_html_unique_nillable_key\" UNIQUE (\"html_unique_nillable\"),CONSTRAINT \"entity_hyperlink_unique_nillable_key\" UNIQUE (\"hyperlink_unique_nillable\"),CONSTRAINT \"entity_int_unique_nillable_key\" UNIQUE (\"int_unique_nillable\"),CONSTRAINT \"entity_long_unique_nillable_key\" UNIQUE (\"long_unique_nillable\"),CONSTRAINT \"entity_script_unique_nillable_key\" UNIQUE (\"script_unique_nillable\"),CONSTRAINT \"entity_string_unique_nillable_key\" UNIQUE (\"string_unique_nillable\"),CONSTRAINT \"entity_text_unique_nillable_key\" UNIQUE (\"text_unique_nillable\"),CONSTRAINT \"entity_xref_unique_nillable_fkey\" FOREIGN KEY (\"xref_unique_nillable\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\"),CONSTRAINT \"entity_xref_unique_nillable_key\" UNIQUE (\"xref_unique_nillable\"))"); + } + @Test public void getSqlCreateForeignKey() { @@ -94,6 +199,46 @@ public void getSqlDropUniqueKey() assertEquals(PostgreSqlQueryGenerator.getSqlDropUniqueKey(entityMeta, attr), expectedSql); } + @Test + public void getSqlCreateCheckConstraint() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(ENUM); + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1", "enum2")); + assertEquals(PostgreSqlQueryGenerator.getSqlCreateCheckConstraint(entityMeta, attr), + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0','enum1','enum2'))"); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlCreateCheckConstraintWrongDataType() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + PostgreSqlQueryGenerator.getSqlCreateCheckConstraint(entityMeta, attr); + } + + @Test + public void getSqlDropCheckConstraint() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(ENUM); + when(attr.getEnumOptions()).thenReturn(newArrayList("enum0", "enum1", "enum2")); + assertEquals(PostgreSqlQueryGenerator.getSqlDropCheckConstraint(entityMeta, attr), + "ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_chk\""); + } + + @Test(expectedExceptions = MolgenisDataException.class) + public void getSqlDropCheckConstraintWrongDataType() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + PostgreSqlQueryGenerator.getSqlDropCheckConstraint(entityMeta, attr); + } + @Test public void getSqlCreateJunctionTable() { @@ -130,7 +275,7 @@ public void getSqlCreateJunctionTableSelfReferencing() } @Test - public void testGetSqlSelectMref() throws Exception + public void getSqlSelectMref() throws Exception { Package package_ = when(mock(Package.class).getName()).thenReturn("org_molgenis").getMock(); @@ -197,15 +342,16 @@ public static Iterator getSqlAddColumnProvider() when(refEntityMetaInt.getIdAttribute()).thenReturn(refIdAttrInt); return Arrays.asList(new Object[] { BOOL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" boolean" }, - new Object[] { CATEGORICAL, true, refEntityMetaInt, "ALTER TABLE \"entity\" ADD \"attr\" integer" }, + new Object[] { CATEGORICAL, true, refEntityMetaInt, + "ALTER TABLE \"entity\" ADD \"attr\" integer,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\")" }, new Object[] { DATE, true, null, "ALTER TABLE \"entity\" ADD \"attr\" date" }, new Object[] { DATE_TIME, true, null, "ALTER TABLE \"entity\" ADD \"attr\" timestamp" }, new Object[] { DECIMAL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" double precision" }, new Object[] { EMAIL, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { ENUM, true, null, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) CHECK (\"attr\" IN ('enum0, enum1'))" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0, enum1'))" }, new Object[] { FILE, true, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { HTML, true, null, "ALTER TABLE \"entity\" ADD \"attr\" text" }, new Object[] { HYPERLINK, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { INT, true, null, "ALTER TABLE \"entity\" ADD \"attr\" integer" }, @@ -214,19 +360,19 @@ public static Iterator getSqlAddColumnProvider() new Object[] { STRING, true, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, new Object[] { TEXT, true, null, "ALTER TABLE \"entity\" ADD \"attr\" text" }, new Object[] { XREF, true, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255)" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255),ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { BOOL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" boolean NOT NULL" }, new Object[] { CATEGORICAL, false, refEntityMetaInt, - "ALTER TABLE \"entity\" ADD \"attr\" integer NOT NULL" }, + "ALTER TABLE \"entity\" ADD \"attr\" integer NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityInt\"(\"refIdAttrInt\")" }, new Object[] { DATE, false, null, "ALTER TABLE \"entity\" ADD \"attr\" date NOT NULL" }, new Object[] { DATE_TIME, false, null, "ALTER TABLE \"entity\" ADD \"attr\" timestamp NOT NULL" }, new Object[] { DECIMAL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" double precision NOT NULL" }, new Object[] { EMAIL, false, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, new Object[] { ENUM, false, null, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL CHECK (\"attr\" IN ('enum0, enum1'))" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_chk\" CHECK (\"attr\" IN ('enum0, enum1'))" }, new Object[] { FILE, false, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }, new Object[] { HTML, false, null, "ALTER TABLE \"entity\" ADD \"attr\" text NOT NULL" }, new Object[] { HYPERLINK, false, null, "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, @@ -237,13 +383,16 @@ public static Iterator getSqlAddColumnProvider() "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }, new Object[] { TEXT, false, null, "ALTER TABLE \"entity\" ADD \"attr\" text NOT NULL" }, new Object[] { XREF, false, refEntityMetaString, - "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL" }).iterator(); + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntityStr\"(\"refIdAttrStr\")" }) + .iterator(); } @Test(dataProvider = "getSqlAddColumnProvider") public void getSqlAddColumn(AttributeType attrType, boolean nillable, EntityMetaData refEntityMeta, String sql) { EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); when(attr.getDataType()).thenReturn(attrType); when(attr.isNillable()).thenReturn(nillable); diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 93cb32ff454..80608f71389 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -410,6 +410,20 @@ public void addAttribute() verify(jdbcTemplate).execute("ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL"); } + @Test + public void addAttributeUnique() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + when(attr.getDataType()).thenReturn(STRING); + when(attr.isUnique()).thenReturn(true); + postgreSqlRepoCollection.addAttribute(entityMeta, attr); + verify(jdbcTemplate).execute( + "ALTER TABLE \"entity\" ADD \"attr\" character varying(255) NOT NULL,ADD CONSTRAINT \"entity_attr_key\" UNIQUE (\"attr\")"); + } + @Test public void addAttributeAbstractEntity() { diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java index aa3a1f4a7a9..b0e91a2c47e 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java @@ -359,63 +359,44 @@ private void validateUpdate(AttributeMetaData currentAttr, AttributeMetaData new } } - private static EnumMap> DATA_TYPE_ALLOWED_TRANSITIONS; + private static EnumMap> DATA_TYPE_DISALLOWED_TRANSITIONS; static { - DATA_TYPE_ALLOWED_TRANSITIONS = new EnumMap<>(AttributeType.class); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(BOOL, EnumSet.of(CATEGORICAL, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF - // not allowed : CATEGORICAL, COMPOUND, FILE - DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL, - EnumSet.of(BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(CATEGORICAL_MREF, EnumSet.of(MREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.noneOf(AttributeType.class)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL, DATE_TIME, HTML, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL, DATE, HTML, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(DECIMAL, EnumSet.of(CATEGORICAL, HTML, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(ENUM, EnumSet.noneOf(AttributeType.class)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(FILE, EnumSet.noneOf(AttributeType.class)); - // not implemented: HTML -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : HTML -> COMPOUND, FILE, HTML - DATA_TYPE_ALLOWED_TRANSITIONS.put(HTML, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, INT, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(SCRIPT, HTML, STRING, TEXT)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(INT, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, LONG, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS - .put(LONG, EnumSet.of(BOOL, CATEGORICAL, DECIMAL, HTML, INT, SCRIPT, STRING, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(MREF, EnumSet.of(CATEGORICAL_MREF)); - // not implemented: SCRIPT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : SCRIPT -> COMPOUND, FILE, SCRIPT - DATA_TYPE_ALLOWED_TRANSITIONS.put(SCRIPT, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, STRING, TEXT, XREF)); - // not implemented: TEXT -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : TEXT -> COMPOUND, FILE, TEXT - // not implemented: STRING -> CATEGORICAL, CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF, XREF - // not allowed : STRING -> COMPOUND, FILE, STRING - DATA_TYPE_ALLOWED_TRANSITIONS.put(STRING, - EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, TEXT, XREF)); - DATA_TYPE_ALLOWED_TRANSITIONS.put(TEXT, - EnumSet.of(CATEGORICAL, BOOL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, XREF)); - // not implemented: CATEGORICAL_MREF, EMAIL, ENUM, HYPERLINK, MREF - // not allowed : COMPOUND, FILE, XREF - DATA_TYPE_ALLOWED_TRANSITIONS.put(XREF, - EnumSet.of(BOOL, CATEGORICAL, DATE, DATE_TIME, DECIMAL, HTML, INT, LONG, SCRIPT, STRING, TEXT)); + // transitions to EMAIL and HYPERLINK not allowed because existing values not checked by PostgreSQL + // transitions to CATEGORICAL_MREF and MREF not allowed because junction tables updated not implemented + // transitions to FILE not allowed because associated file in FileStore not created/removed + DATA_TYPE_DISALLOWED_TRANSITIONS = new EnumMap<>(AttributeType.class); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(BOOL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(CATEGORICAL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(CATEGORICAL_MREF, EnumSet.complementOf(EnumSet.of(MREF))); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(COMPOUND, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DATE, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DATE_TIME, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(DECIMAL, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(EMAIL, EnumSet.of(CATEGORICAL_MREF, MREF, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(ENUM, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(FILE, EnumSet.allOf(AttributeType.class)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(HTML, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(HYPERLINK, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(INT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(LONG, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(MREF, EnumSet.complementOf(EnumSet.of(CATEGORICAL_MREF))); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(SCRIPT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(STRING, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(TEXT, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); + DATA_TYPE_DISALLOWED_TRANSITIONS.put(XREF, EnumSet.of(CATEGORICAL_MREF, MREF, EMAIL, HYPERLINK, FILE)); } private static void validateUpdateDataType(AttributeType currentDataType, AttributeType newDataType) { - EnumSet allowedDataTypes = DATA_TYPE_ALLOWED_TRANSITIONS.get(currentDataType); - if (allowedDataTypes == null || !allowedDataTypes.contains(newDataType)) + EnumSet disallowedDataTypes = DATA_TYPE_DISALLOWED_TRANSITIONS.get(currentDataType); + if (disallowedDataTypes.contains(newDataType)) { throw new MolgenisDataException( format("Attribute data type update from [%s] to [%s] not allowed, allowed types are %s", currentDataType.toString(), newDataType.toString(), - allowedDataTypes != null ? allowedDataTypes.toString() : "[]")); + EnumSet.complementOf(disallowedDataTypes).toString())); } } From 56839c922fcd3d4c0d14f9870ae7826bed078916 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 09:30:30 +0200 Subject: [PATCH 17/49] ENH: add labels for all system entities Set EntityMetaDataMetaData.LABEL to required Use simpleName by default in EntityMetaData Consistency: rename *EntityMetaData classes to *MetaData classes --- .../meta/AnnotationJobExecutionMetaData.java | 1 + .../ThousendGenomesAnnotatorSettings.java | 1 + .../l2/settings/L2CacheSettingsMetaData.java | 2 +- .../logback/LoggingEventMetaData.java | 1 + .../reindex/meta/ReindexJobExecutionMeta.java | 1 + .../model/IdCardIndexingEventMetaData.java | 1 + .../mapper/meta/AttributeMappingMetaData.java | 1 + .../mapper/meta/EntityMappingMetaData.java | 1 + .../mapper/meta/MappingProjectMetaData.java | 1 + .../mapper/meta/MappingTargetMetaData.java | 1 + .../org/molgenis/auth/AuthorityMetaData.java | 1 + .../molgenis/auth/GroupAuthorityMetaData.java | 1 + .../auth/MolgenisGroupMemberMetaData.java | 1 + .../molgenis/auth/MolgenisGroupMetaData.java | 1 + .../molgenis/auth/MolgenisTokenMetaData.java | 1 + .../molgenis/auth/MolgenisUserMetaData.java | 1 + .../molgenis/auth/UserAuthorityMetaData.java | 1 + .../data/meta/model/EntityMetaData.java | 16 +++-- .../meta/model/EntityMetaDataMetaData.java | 2 +- .../meta/ReindexActionGroupMetaData.java | 1 + .../reindex/meta/ReindexActionMetaData.java | 1 + .../data/settings/SettingsEntityMeta.java | 1 + .../core/FreemarkerTemplateMetaData.java | 1 + .../data/meta/model/EntityMetaDataTest.java | 66 ++++++++++++++++--- .../meta/FileIngestJobExecutionMetaData.java | 1 + .../file/ingest/meta/FileIngestMetaData.java | 1 + .../molgenis/file/model/FileMetaMetaData.java | 1 + .../job/meta/GavinJobExecutionMetaData.java | 1 + .../data/jobs/model/JobExecutionMetaData.java | 1 + .../ontology/core/meta/OntologyMetaData.java | 1 + ...OntologyTermDynamicAnnotationMetaData.java | 1 + .../core/meta/OntologyTermMetaData.java | 1 + .../meta/OntologyTermNodePathMetaData.java | 1 + .../meta/OntologyTermSynonymMetaData.java | 1 + .../ic/OntologyTermFrequencyServiceImpl.java | 9 ++- ...taData.java => TermFrequencyMetaData.java} | 5 +- .../molgenis/ontology/SortaConfiguration.java | 6 +- .../SortaServiceAnonymousController.java | 4 +- .../controller/SortaServiceController.java | 39 +++++------ .../ontology/sorta/job/SortaJobProcessor.java | 24 +++---- ....java => MatchingTaskContentMetaData.java} | 5 +- ...Data.java => OntologyTermHitMetaData.java} | 5 +- .../sorta/meta/SortaJobExecutionMetaData.java | 1 + .../sorta/service/impl/SortaServiceImpl.java | 16 ++--- .../ontology/sorta/SortaServiceImplTest.java | 8 +-- .../questionnaires/QuestionnaireMetaData.java | 1 + .../org/molgenis/script/ScriptMetaData.java | 1 + .../script/ScriptParameterMetaData.java | 1 + .../molgenis/script/ScriptTypeMetaData.java | 1 + .../security/owned/OwnedEntityMetaData.java | 1 + 50 files changed, 168 insertions(+), 75 deletions(-) rename molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/{TermFrequencyEntityMetaData.java => TermFrequencyMetaData.java} (90%) rename molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/{MatchingTaskContentEntityMetaData.java => MatchingTaskContentMetaData.java} (91%) rename molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/{OntologyTermHitEntityMetaData.java => OntologyTermHitMetaData.java} (88%) diff --git a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/meta/AnnotationJobExecutionMetaData.java b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/meta/AnnotationJobExecutionMetaData.java index 774b074edcf..5322cba8d38 100644 --- a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/meta/AnnotationJobExecutionMetaData.java +++ b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/meta/AnnotationJobExecutionMetaData.java @@ -31,6 +31,7 @@ public class AnnotationJobExecutionMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Annotation job execution"); setExtends(jobExecutionMetaData); addAttribute(TARGET_NAME).setDataType(STRING).setLabel("target name") .setDescription("Fully qualified name of the entity that is being annotated.").setNillable(false); diff --git a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/settings/ThousendGenomesAnnotatorSettings.java b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/settings/ThousendGenomesAnnotatorSettings.java index 5ff11a49efa..478b09b5335 100644 --- a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/settings/ThousendGenomesAnnotatorSettings.java +++ b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/web/settings/ThousendGenomesAnnotatorSettings.java @@ -33,6 +33,7 @@ public Meta() public void init() { super.init(); + setLabel("1000 Genomes annotator settings"); addAttribute(CHROMOSOMES).setLabel("Chromosomes") .setDefaultValue("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22"); addAttribute(FILEPATTERN).setLabel("Filepattern") diff --git a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/settings/L2CacheSettingsMetaData.java b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/settings/L2CacheSettingsMetaData.java index 57774204abe..9e7ef416e0d 100644 --- a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/settings/L2CacheSettingsMetaData.java +++ b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/settings/L2CacheSettingsMetaData.java @@ -46,8 +46,8 @@ public L2CacheSettingsMetaData(RootSystemPackage rootSystemPackage, EntityMetaDa @Override public void init() { + setLabel("L2 cache settings"); setPackage(rootSystemPackage); - setDescription("L2 Cache settings"); addAttribute(ID, ROLE_ID).setAuto(true).setVisible(false) .setDescription("automatically generated internal id, only for internal use."); addAttribute(CACHED_ENTITY).setDescription("Name of the entity whose L2 Cache settings these are.") diff --git a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java b/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java index 4941df42bb8..21e9721c406 100644 --- a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java +++ b/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java @@ -31,6 +31,7 @@ public class LoggingEventMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Logging event"); addAttribute(IDENTIFIER, ROLE_ID).setVisible(false); addAttribute(TIMESTAMP).setDataType(DATE_TIME); addAttribute(THREAD); diff --git a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/reindex/meta/ReindexJobExecutionMeta.java b/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/reindex/meta/ReindexJobExecutionMeta.java index 5ec67ba3686..b3099f97adc 100644 --- a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/reindex/meta/ReindexJobExecutionMeta.java +++ b/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/reindex/meta/ReindexJobExecutionMeta.java @@ -42,6 +42,7 @@ public ReindexJobExecutionMeta(IndexPackage indexPackage, JobExecutionMetaData j @Override public void init() { + setLabel("Index job execution"); setPackage(indexPackage); setExtends(jobExecutionMetaData); diff --git a/molgenis-data-idcard/src/main/java/org/molgenis/data/idcard/model/IdCardIndexingEventMetaData.java b/molgenis-data-idcard/src/main/java/org/molgenis/data/idcard/model/IdCardIndexingEventMetaData.java index 7659cf8b828..b43474a6bd1 100644 --- a/molgenis-data-idcard/src/main/java/org/molgenis/data/idcard/model/IdCardIndexingEventMetaData.java +++ b/molgenis-data-idcard/src/main/java/org/molgenis/data/idcard/model/IdCardIndexingEventMetaData.java @@ -36,6 +36,7 @@ public class IdCardIndexingEventMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("ID-Card indexing event"); setPackage(idCardPackage); addAttribute(ID, ROLE_ID).setVisible(false).setAuto(true).setLabel("Id"); diff --git a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/AttributeMappingMetaData.java b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/AttributeMappingMetaData.java index ad5436f100e..463f79c62a7 100644 --- a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/AttributeMappingMetaData.java +++ b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/AttributeMappingMetaData.java @@ -40,6 +40,7 @@ public AttributeMappingMetaData(MapperPackage mapperPackage) @Override public void init() { + setLabel("Attribute mapping"); setPackage(mapperPackage); addAttribute(IDENTIFIER, ROLE_ID); diff --git a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/EntityMappingMetaData.java b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/EntityMappingMetaData.java index 2435962db89..fc41b789127 100644 --- a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/EntityMappingMetaData.java +++ b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/EntityMappingMetaData.java @@ -35,6 +35,7 @@ public EntityMappingMetaData(MapperPackage mapperPackage, AttributeMappingMetaDa @Override public void init() { + setLabel("Entity mapping"); setPackage(mapperPackage); addAttribute(IDENTIFIER, ROLE_ID); diff --git a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingProjectMetaData.java b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingProjectMetaData.java index 8a6f0b48f01..4d73e46e0c3 100644 --- a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingProjectMetaData.java +++ b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingProjectMetaData.java @@ -40,6 +40,7 @@ public MappingProjectMetaData(MapperPackage mapperPackage, MolgenisUserMetaData @Override public void init() { + setLabel("Mapping project"); setPackage(mapperPackage); addAttribute(IDENTIFIER, ROLE_ID); diff --git a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingTargetMetaData.java b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingTargetMetaData.java index 54c0604c223..794b6fa8c34 100644 --- a/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingTargetMetaData.java +++ b/molgenis-data-mapper/src/main/java/org/molgenis/data/mapper/meta/MappingTargetMetaData.java @@ -34,6 +34,7 @@ public MappingTargetMetaData(MapperPackage mapperPackage, EntityMappingMetaData @Override public void init() { + setLabel("Mapping target"); setPackage(mapperPackage); addAttribute(IDENTIFIER, ROLE_ID); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/AuthorityMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/AuthorityMetaData.java index d6f77e76e83..9d78534e9a1 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/AuthorityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/AuthorityMetaData.java @@ -28,6 +28,7 @@ public class AuthorityMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Authority"); setPackage(securityPackage); setAbstract(true); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java index 2406542b699..80cc938a754 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java @@ -40,6 +40,7 @@ public class GroupAuthorityMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Group authority"); setPackage(securityPackage); setExtends(authorityMetaData); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMemberMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMemberMetaData.java index ef96f57771a..a821b437913 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMemberMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMemberMetaData.java @@ -37,6 +37,7 @@ public class MolgenisGroupMemberMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Group member"); setPackage(securityPackage); addAttribute(ID, ROLE_ID).setAuto(true).setVisible(false).setDescription(""); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMetaData.java index 3369ff1f1eb..01365d44871 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisGroupMetaData.java @@ -37,6 +37,7 @@ public class MolgenisGroupMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Group"); setPackage(securityPackage); setExtends(authorityMetaData); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisTokenMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisTokenMetaData.java index f5959d201cf..c96e2f72942 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisTokenMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisTokenMetaData.java @@ -38,6 +38,7 @@ public class MolgenisTokenMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Token"); setPackage(securityPackage); addAttribute(ID, ROLE_ID).setAuto(true).setVisible(false).setDescription(""); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisUserMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisUserMetaData.java index 726f63e4630..e6ff654e492 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/MolgenisUserMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/MolgenisUserMetaData.java @@ -54,6 +54,7 @@ public MolgenisUserMetaData(SecurityPackage securityPackage) @Override public void init() { + setLabel("User"); setPackage(securityPackage); setDescription("Anyone who can login"); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/UserAuthorityMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/UserAuthorityMetaData.java index 6ed876a9c33..7473e9bf05d 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/UserAuthorityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/UserAuthorityMetaData.java @@ -39,6 +39,7 @@ public class UserAuthorityMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("User authority"); setPackage(securityPackage); setExtends(authorityMetaData); diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java index f18b7e0f407..ddc9339aa6e 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java @@ -172,18 +172,22 @@ public EntityMetaData setSimpleName(String simpleName) { set(SIMPLE_NAME, simpleName); updateFullName(); + + if (getLabel() == null) + { + setLabel(simpleName); + } return this; } /** - * Optional human readable longer label + * Human readable entity label * * @return entity label */ public String getLabel() { - String label = getString(LABEL); - return label != null ? label : getString(FULL_NAME); + return getString(LABEL); } /** @@ -199,6 +203,10 @@ public String getLabel(String languageCode) public EntityMetaData setLabel(String label) { + if (label == null) + { + label = getSimpleName(); + } set(LABEL, label); return this; } @@ -848,7 +856,7 @@ private void invalidateCachedAttrs() public enum AttributeRole { - ROLE_ID, ROLE_LABEL, ROLE_LOOKUP + ROLE_ID, ROLE_LABEL, ROLE_LOOKUP; } @Override diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java index 168f966968f..7cd2c6a6018 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java @@ -49,7 +49,7 @@ public void init() addAttribute(SIMPLE_NAME, ROLE_LABEL).setNillable(false).setReadOnly(true).setLabel("Name"); // TODO discuss whether package should be nillable addAttribute(PACKAGE).setDataType(XREF).setRefEntity(packageMetaData).setLabel("Package").setReadOnly(true); - addAttribute(LABEL, ROLE_LOOKUP).setLabel("Label"); + addAttribute(LABEL, ROLE_LOOKUP).setNillable(false).setLabel("Label"); addAttribute(DESCRIPTION).setDataType(TEXT).setLabel("Description"); addAttribute(ATTRIBUTES).setDataType(MREF).setRefEntity(attrMetaMeta).setNillable(false).setLabel("Attributes"); addAttribute(ID_ATTRIBUTE).setDataType(XREF).setRefEntity(attrMetaMeta).setLabel("ID attribute"); diff --git a/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionGroupMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionGroupMetaData.java index 8aaea11c2df..6d2d1b65f35 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionGroupMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionGroupMetaData.java @@ -41,6 +41,7 @@ public ReindexActionGroupMetaData(IndexPackage indexPackage) @Override public void init() { + setLabel("Index action group"); setPackage(indexPackage); setDescription("This entity is used to group the reindex actions."); diff --git a/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionMetaData.java index de1d181b863..562db3e22ab 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/reindex/meta/ReindexActionMetaData.java @@ -80,6 +80,7 @@ public ReindexActionMetaData(IndexPackage indexPackage, ReindexActionGroupMetaDa @Override public void init() { + setLabel("Index action"); setPackage(indexPackage); addAttribute(ID, ROLE_ID).setAuto(true).setVisible(false); diff --git a/molgenis-data/src/main/java/org/molgenis/data/settings/SettingsEntityMeta.java b/molgenis-data/src/main/java/org/molgenis/data/settings/SettingsEntityMeta.java index 920b394f717..1078b786136 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/settings/SettingsEntityMeta.java +++ b/molgenis-data/src/main/java/org/molgenis/data/settings/SettingsEntityMeta.java @@ -26,6 +26,7 @@ public SettingsEntityMeta() @Override public void init() { + setLabel("Settings"); setAbstract(true); setPackage(settingsPackage); addAttribute(ID, ROLE_ID).setLabel("Id").setVisible(false); diff --git a/molgenis-data/src/main/java/org/molgenis/data/system/core/FreemarkerTemplateMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/system/core/FreemarkerTemplateMetaData.java index 398467b766a..1c2fd095554 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/system/core/FreemarkerTemplateMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/system/core/FreemarkerTemplateMetaData.java @@ -27,6 +27,7 @@ public class FreemarkerTemplateMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Freemarker template"); addAttribute(ID, ROLE_ID).setAuto(true).setVisible(false) .setDescription("automatically generated internal id, only for internal use."); addAttribute(NAME, ROLE_LABEL).setDescription("Name of the entity").setNillable(false).setUnique(true); diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java index 31ea0e0fac5..728e060639c 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.molgenis.MolgenisFieldTypes.AttributeType.*; -import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ATTRIBUTES; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; import static org.testng.Assert.*; public class EntityMetaDataTest @@ -23,13 +23,11 @@ public class EntityMetaDataTest private AttributeMetaData randomAttribute; private AttributeMetaData compoundAttribute; private AttributeMetaData attributePart; - private Entity entity; private EntityMetaData nestedEntityMetaData; private AttributeMetaData nestedCompoundParent; private AttributeMetaData nestedCompoundPart; private AttributeMetaData nestedAttributePart; - private Entity nestedEntity; @BeforeMethod public void beforeMethod() @@ -48,8 +46,8 @@ public void beforeMethod() Iterable mockedAttributes = newArrayList(compoundAttribute, randomAttribute); - entity = when(mock(Entity.class).getEntities(ATTRIBUTES, AttributeMetaData.class)).thenReturn(mockedAttributes) - .getMock(); + Entity entity = when(mock(Entity.class).getEntities(ATTRIBUTES, AttributeMetaData.class)) + .thenReturn(mockedAttributes).getMock(); entityMetaData = new EntityMetaData(entity); // Setup for nested compound test @@ -65,11 +63,63 @@ public void beforeMethod() Iterable mockedNestedAttributes = newArrayList(nestedCompoundParent, randomAttribute); - nestedEntity = when(mock(Entity.class).getEntities(ATTRIBUTES, AttributeMetaData.class)) + Entity nestedEntity = when(mock(Entity.class).getEntities(ATTRIBUTES, AttributeMetaData.class)) .thenReturn(mockedNestedAttributes).getMock(); nestedEntityMetaData = new EntityMetaData(nestedEntity); } + @Test + public void setSimpleNameNoNameNoLabel() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String simpleName = "simpleName"; + entityMeta.setSimpleName(simpleName); + assertEquals(entityMeta.getSimpleName(), simpleName); + assertEquals(entityMeta.getString(SIMPLE_NAME), simpleName); + assertEquals(entityMeta.getName(), simpleName); + assertEquals(entityMeta.getString(FULL_NAME), simpleName); + assertEquals(entityMeta.getLabel(), simpleName); + assertEquals(entityMeta.getString(LABEL), simpleName); + } + + @Test + public void setSimpleNameExistingNameExistingLabel() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String label = "label"; + String simpleName = "simpleName"; + entityMeta.setName("name"); + entityMeta.setLabel(label); + entityMeta.setSimpleName(simpleName); + assertEquals(entityMeta.getSimpleName(), simpleName); + assertEquals(entityMeta.getString(SIMPLE_NAME), simpleName); + assertEquals(entityMeta.getName(), simpleName); + assertEquals(entityMeta.getString(FULL_NAME), simpleName); + assertEquals(entityMeta.getLabel(), label); + assertEquals(entityMeta.getString(LABEL), label); + } + + @Test + public void setLabel() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String label = "label"; + entityMeta.setLabel(label); + assertEquals(entityMeta.getLabel(), label); + assertEquals(entityMeta.getString(LABEL), label); + } + + @Test + public void setLabelNull() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String simpleName = "simpleName"; + entityMeta.setSimpleName(simpleName); + entityMeta.setLabel(null); + assertEquals(entityMeta.getLabel(), simpleName); + assertEquals(entityMeta.getString(LABEL), simpleName); + } + @Test public void getCompoundOrderedAttributesCorrectOrderTest() { @@ -180,7 +230,7 @@ public void newInstanceShallowCopy() assertEquals(entityMetaCopy.getBackend(), "backend"); } - private EntityMetaData createEntityMetaMeta() + private static EntityMetaData createEntityMetaMeta() { EntityMetaData entityMetaMeta = mock(EntityMetaData.class); AttributeMetaData strAttr = when(mock(AttributeMetaData.class).getDataType()).thenReturn(STRING).getMock(); @@ -190,7 +240,7 @@ private EntityMetaData createEntityMetaMeta() when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.FULL_NAME)).thenReturn(strAttr); when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.SIMPLE_NAME)).thenReturn(strAttr); when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.PACKAGE)).thenReturn(xrefAttr); - when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.LABEL)).thenReturn(strAttr); + when(entityMetaMeta.getAttribute(LABEL)).thenReturn(strAttr); when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.DESCRIPTION)).thenReturn(strAttr); when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.ATTRIBUTES)).thenReturn(mrefAttr); when(entityMetaMeta.getAttribute(EntityMetaDataMetaData.ID_ATTRIBUTE)).thenReturn(xrefAttr); diff --git a/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestJobExecutionMetaData.java b/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestJobExecutionMetaData.java index 3f0e6a45854..ee710af00a0 100644 --- a/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestJobExecutionMetaData.java +++ b/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestJobExecutionMetaData.java @@ -39,6 +39,7 @@ public class FileIngestJobExecutionMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("File ingest job execution"); setExtends(jobExecutionMetaData); addAttribute(FILE).setLabel("File").setDescription("The imported file.").setDataType(XREF) .setRefEntity(fileMetaMetaData).setNillable(true); diff --git a/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestMetaData.java b/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestMetaData.java index 95351d0db06..a703e8cf1bd 100644 --- a/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestMetaData.java +++ b/molgenis-file-ingester/src/main/java/org/molgenis/file/ingest/meta/FileIngestMetaData.java @@ -41,6 +41,7 @@ public FileIngestMetaData(EntityMetaDataMetaData entityMetaDataMetaData) @Override public void init() { + setLabel("File ingest"); addAttribute(ID, ROLE_ID).setAuto(true).setNillable(false); addAttribute(NAME, ROLE_LABEL, ROLE_LOOKUP).setLabel("Name").setNillable(false); addAttribute(DESCRIPTION).setDataType(TEXT).setLabel("Description").setNillable(true); diff --git a/molgenis-file/src/main/java/org/molgenis/file/model/FileMetaMetaData.java b/molgenis-file/src/main/java/org/molgenis/file/model/FileMetaMetaData.java index 3b738aa2b44..b6fa0e5bfba 100644 --- a/molgenis-file/src/main/java/org/molgenis/file/model/FileMetaMetaData.java +++ b/molgenis-file/src/main/java/org/molgenis/file/model/FileMetaMetaData.java @@ -38,6 +38,7 @@ public class FileMetaMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("File metadata"); setExtends(ownedEntityMetaData); addAttribute(ID, ROLE_ID).setVisible(false).setLabel("Id"); addAttribute(FILENAME, ROLE_LABEL, ROLE_LOOKUP).setDataType(STRING).setNillable(false).setLabel("Filename"); diff --git a/molgenis-gavin/src/main/java/org/molgenis/gavin/job/meta/GavinJobExecutionMetaData.java b/molgenis-gavin/src/main/java/org/molgenis/gavin/job/meta/GavinJobExecutionMetaData.java index ccf4a43ca24..55eca6c1883 100644 --- a/molgenis-gavin/src/main/java/org/molgenis/gavin/job/meta/GavinJobExecutionMetaData.java +++ b/molgenis-gavin/src/main/java/org/molgenis/gavin/job/meta/GavinJobExecutionMetaData.java @@ -37,6 +37,7 @@ public GavinJobExecutionMetaData(IndexPackage indexPackage, JobExecutionMetaData @Override public void init() { + setLabel("Gavin job execution"); setPackage(indexPackage); setExtends(jobExecutionMetaData); diff --git a/molgenis-jobs/src/main/java/org/molgenis/data/jobs/model/JobExecutionMetaData.java b/molgenis-jobs/src/main/java/org/molgenis/data/jobs/model/JobExecutionMetaData.java index ccac23b413d..556a272bd3f 100644 --- a/molgenis-jobs/src/main/java/org/molgenis/data/jobs/model/JobExecutionMetaData.java +++ b/molgenis-jobs/src/main/java/org/molgenis/data/jobs/model/JobExecutionMetaData.java @@ -42,6 +42,7 @@ public class JobExecutionMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Job execution"); setAbstract(true); addAttribute(IDENTIFIER, ROLE_ID).setLabel("Job ID").setAuto(true).setNillable(false); addAttribute(USER).setDataType(STRING).setLabel("Job owner").setNillable(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyMetaData.java index 26d2dec6357..98337d34a49 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyMetaData.java @@ -33,6 +33,7 @@ public OntologyMetaData(OntologyPackage ontologyPackage) @Override public void init() { + setLabel("Ontology"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setVisible(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermDynamicAnnotationMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermDynamicAnnotationMetaData.java index 5fb07c9539a..05d50bce023 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermDynamicAnnotationMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermDynamicAnnotationMetaData.java @@ -34,6 +34,7 @@ public OntologyTermDynamicAnnotationMetaData(OntologyPackage ontologyPackage) @Override public void init() { + setLabel("Ontology term dynamic annotation"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setVisible(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermMetaData.java index 6917e18112c..03d6a70a468 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermMetaData.java @@ -49,6 +49,7 @@ public OntologyTermMetaData(OntologyPackage ontologyPackage, @Override public void init() { + setLabel("Ontology term"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setVisible(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermNodePathMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermNodePathMetaData.java index 77f0a790863..7c965d9497d 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermNodePathMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermNodePathMetaData.java @@ -33,6 +33,7 @@ public OntologyTermNodePathMetaData() @Override public void init() { + setLabel("Ontology term node path"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setVisible(false); addAttribute(NODE_PATH, ROLE_LABEL).setDataType(TEXT).setNillable(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermSynonymMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermSynonymMetaData.java index 859d77c7d28..0aa3d33966a 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermSynonymMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/core/meta/OntologyTermSynonymMetaData.java @@ -33,6 +33,7 @@ public OntologyTermSynonymMetaData() @Override public void init() { + setLabel("Ontology term synonym"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setVisible(false); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/OntologyTermFrequencyServiceImpl.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/OntologyTermFrequencyServiceImpl.java index cc95e749470..f9f751163fc 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/OntologyTermFrequencyServiceImpl.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/OntologyTermFrequencyServiceImpl.java @@ -13,7 +13,7 @@ import java.util.List; import static org.molgenis.ontology.core.meta.OntologyTermSynonymMetaData.ONTOLOGY_TERM_SYNONYM; -import static org.molgenis.ontology.ic.TermFrequencyEntityMetaData.*; +import static org.molgenis.ontology.ic.TermFrequencyMetaData.*; import static org.molgenis.util.ApplicationContextProvider.getApplicationContext; public class OntologyTermFrequencyServiceImpl implements TermFrequencyService @@ -60,8 +60,7 @@ private Entity addEntry(String term, PubMedTFEntity pubMedTFEntity, DataService if (pubMedTFEntity == null) return null; // FIXME remove reference to getApplicationContext - TermFrequencyEntityMetaData termFrequencyEntityMeta = getApplicationContext() - .getBean(TermFrequencyEntityMetaData.class); + TermFrequencyMetaData termFrequencyEntityMeta = getApplicationContext().getBean(TermFrequencyMetaData.class); Entity entity = new DynamicEntity(termFrequencyEntityMeta); entity.set(TERM, term); entity.set(FREQUENCY, pubMedTFEntity.getFrequency()); @@ -88,8 +87,8 @@ public void updateTermFrequency() if (pubMedTFEntity != null) { // FIXME remove reference to getApplicationContext - TermFrequencyEntityMetaData termFrequencyEntityMeta = getApplicationContext() - .getBean(TermFrequencyEntityMetaData.class); + TermFrequencyMetaData termFrequencyEntityMeta = getApplicationContext() + .getBean(TermFrequencyMetaData.class); Entity mapEntity = new DynamicEntity(termFrequencyEntityMeta); mapEntity.set(TERM, ontologyTermSynonym); mapEntity.set(FREQUENCY, pubMedTFEntity.getFrequency()); diff --git a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyEntityMetaData.java b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyMetaData.java similarity index 90% rename from molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyEntityMetaData.java rename to molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyMetaData.java index b3c39acfb20..522a4e0568c 100644 --- a/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyEntityMetaData.java +++ b/molgenis-ontology-core/src/main/java/org/molgenis/ontology/ic/TermFrequencyMetaData.java @@ -12,7 +12,7 @@ import static org.molgenis.ontology.core.model.OntologyPackage.PACKAGE_ONTOLOGY; @Component -public class TermFrequencyEntityMetaData extends SystemEntityMetaData +public class TermFrequencyMetaData extends SystemEntityMetaData { public static final String SIMPLE_NAME = "TermFrequency"; public static final String TERM_FREQUENCY = PACKAGE_ONTOLOGY + PACKAGE_SEPARATOR + SIMPLE_NAME; @@ -25,7 +25,7 @@ public class TermFrequencyEntityMetaData extends SystemEntityMetaData private final OntologyPackage ontologyPackage; @Autowired - TermFrequencyEntityMetaData(OntologyPackage ontologyPackage) + TermFrequencyMetaData(OntologyPackage ontologyPackage) { super(SIMPLE_NAME, PACKAGE_ONTOLOGY); this.ontologyPackage = ontologyPackage; @@ -34,6 +34,7 @@ public class TermFrequencyEntityMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Term frequency"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setAuto(true); diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/SortaConfiguration.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/SortaConfiguration.java index a5f625c31ee..3d8996529bf 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/SortaConfiguration.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/SortaConfiguration.java @@ -5,7 +5,7 @@ import org.molgenis.ontology.ic.OntologyTermFrequencyServiceImpl; import org.molgenis.ontology.ic.TermFrequencyService; import org.molgenis.ontology.roc.InformationContentService; -import org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData; +import org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData; import org.molgenis.ontology.sorta.service.SortaService; import org.molgenis.ontology.sorta.service.impl.SortaServiceImpl; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +19,7 @@ public class SortaConfiguration private DataService dataService; @Autowired - private OntologyTermHitEntityMetaData ontologyTermHitEntityMetaData; + private OntologyTermHitMetaData ontologyTermHitMetaData; @Autowired private OntologyTermSynonymFactory ontologyTermSynonymFactory; @@ -33,7 +33,7 @@ public TermFrequencyService termFrequencyService() @Bean public SortaService sortaService() { - return new SortaServiceImpl(dataService, informationContentService(), ontologyTermHitEntityMetaData, + return new SortaServiceImpl(dataService, informationContentService(), ontologyTermHitMetaData, ontologyTermSynonymFactory); } diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceAnonymousController.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceAnonymousController.java index a0937d6fcf2..8e4af0b6eb2 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceAnonymousController.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceAnonymousController.java @@ -42,8 +42,8 @@ import java.util.*; import static org.molgenis.ontology.controller.SortaServiceAnonymousController.URI; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.COMBINED_SCORE; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.COMBINED_SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.SCORE; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceController.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceController.java index cc33041655f..2fd3668cbbc 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceController.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/controller/SortaServiceController.java @@ -25,7 +25,7 @@ import org.molgenis.ontology.sorta.job.SortaJobExecutionFactory; import org.molgenis.ontology.sorta.job.SortaJobFactory; import org.molgenis.ontology.sorta.job.SortaJobImpl; -import org.molgenis.ontology.sorta.meta.MatchingTaskContentEntityMetaData; +import org.molgenis.ontology.sorta.meta.MatchingTaskContentMetaData; import org.molgenis.ontology.sorta.meta.SortaJobExecutionMetaData; import org.molgenis.ontology.sorta.repo.SortaCsvRepository; import org.molgenis.ontology.sorta.request.SortaServiceRequest; @@ -74,7 +74,7 @@ import static org.molgenis.data.Sort.Direction.DESC; import static org.molgenis.data.meta.model.EntityMetaData.AttributeCopyMode.DEEP_COPY_ATTRS; import static org.molgenis.ontology.controller.SortaServiceController.URI; -import static org.molgenis.ontology.sorta.meta.MatchingTaskContentEntityMetaData.*; +import static org.molgenis.ontology.sorta.meta.MatchingTaskContentMetaData.*; import static org.molgenis.ontology.sorta.meta.SortaJobExecutionMetaData.SORTA_JOB_EXECUTION; import static org.molgenis.ontology.utils.SortaServiceUtil.getEntityAsMap; import static org.springframework.web.bind.annotation.RequestMethod.GET; @@ -97,7 +97,7 @@ public class SortaServiceController extends MolgenisPluginController private final MenuReaderService menuReaderService; private final IdGenerator idGenerator; private final PermissionSystemService permissionSystemService; - private final MatchingTaskContentEntityMetaData matchingTaskContentEntityMetaData; + private final MatchingTaskContentMetaData matchingTaskContentMetaData; private final SortaJobExecutionMetaData sortaJobExecutionMetaData; private final OntologyTermMetaData ontologyTermMetaData; private final SortaJobExecutionFactory sortaJobExecutionFactory; @@ -116,8 +116,7 @@ public SortaServiceController(OntologyService ontologyService, SortaService sort SortaJobFactory sortaMatchJobFactory, ExecutorService taskExecutor, UserAccountService userAccountService, FileStore fileStore, MolgenisPermissionService molgenisPermissionService, DataService dataService, LanguageService languageService, MenuReaderService menuReaderService, IdGenerator idGenerator, - PermissionSystemService permissionSystemService, - MatchingTaskContentEntityMetaData matchingTaskContentEntityMetaData, + PermissionSystemService permissionSystemService, MatchingTaskContentMetaData matchingTaskContentMetaData, SortaJobExecutionMetaData sortaJobExecutionMetaData, OntologyTermMetaData ontologyTermMetaData, SortaJobExecutionFactory sortaJobExecutionFactory, EntityMetaDataFactory entityMetaFactory, AttributeMetaDataFactory attrMetaFactory) @@ -135,7 +134,7 @@ public SortaServiceController(OntologyService ontologyService, SortaService sort this.menuReaderService = requireNonNull(menuReaderService); this.idGenerator = requireNonNull(idGenerator); this.permissionSystemService = requireNonNull(permissionSystemService); - this.matchingTaskContentEntityMetaData = requireNonNull(matchingTaskContentEntityMetaData); + this.matchingTaskContentMetaData = requireNonNull(matchingTaskContentMetaData); this.sortaJobExecutionMetaData = requireNonNull(sortaJobExecutionMetaData); this.ontologyTermMetaData = requireNonNull(ontologyTermMetaData); this.sortaJobExecutionFactory = requireNonNull(sortaJobExecutionFactory); @@ -313,7 +312,7 @@ public EntityCollectionResponse retrieveSortaJobResults(@RequestBody SortaServic .map(inputEntity -> inputEntity.getString(SortaServiceImpl.DEFAULT_MATCHING_IDENTIFIER)) .collect(Collectors.toList()); QueryRule previousQueryRule = new QueryRule(queryRuleInputEntitiesInOneMatchingTask); - QueryRule queryRuleFilterInput = new QueryRule(MatchingTaskContentEntityMetaData.INPUT_TERM, Operator.IN, + QueryRule queryRuleFilterInput = new QueryRule(MatchingTaskContentMetaData.INPUT_TERM, Operator.IN, filteredInputTermIds); queryRuleInputEntitiesInOneMatchingTask = Arrays .asList(previousQueryRule, new QueryRule(Operator.AND), queryRuleFilterInput); @@ -374,14 +373,14 @@ public SortaServiceResponse findMatchingOntologyTerms(@RequestBody Map()) @@ -555,8 +553,7 @@ private SortaJobExecution createJobExecution(Repository inputData, Strin private void createEmptyResultRepository(String jobName, String resultEntityName, EntityMetaData sourceMetaData) { - EntityMetaData resultEntityMetaData = EntityMetaData - .newInstance(matchingTaskContentEntityMetaData, DEEP_COPY_ATTRS); + EntityMetaData resultEntityMetaData = EntityMetaData.newInstance(matchingTaskContentMetaData, DEEP_COPY_ATTRS); resultEntityMetaData.setName(resultEntityName); resultEntityMetaData.setAbstract(false); resultEntityMetaData.addAttribute( @@ -578,8 +575,8 @@ private void createInputRepository(Repository inputRepository) private long countMatchedEntities(SortaJobExecution sortaJobExecution, boolean isMatched) { double threshold = sortaJobExecution.getThreshold(); - QueryRule validatedRule = new QueryRule(MatchingTaskContentEntityMetaData.VALIDATED, EQUALS, isMatched); - QueryRule thresholdRule = new QueryRule(MatchingTaskContentEntityMetaData.SCORE, + QueryRule validatedRule = new QueryRule(MatchingTaskContentMetaData.VALIDATED, EQUALS, isMatched); + QueryRule thresholdRule = new QueryRule(MatchingTaskContentMetaData.SCORE, isMatched ? GREATER_EQUAL : LESS, threshold); QueryRule combinedRule = new QueryRule( asList(validatedRule, new QueryRule(isMatched ? OR : AND), thresholdRule)); diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/job/SortaJobProcessor.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/job/SortaJobProcessor.java index bf06bec137b..7fb9a2e97f5 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/job/SortaJobProcessor.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/job/SortaJobProcessor.java @@ -9,7 +9,7 @@ import org.molgenis.data.support.QueryImpl; import org.molgenis.ontology.controller.SortaServiceController; import org.molgenis.ontology.core.meta.OntologyTermMetaData; -import org.molgenis.ontology.sorta.meta.MatchingTaskContentEntityMetaData; +import org.molgenis.ontology.sorta.meta.MatchingTaskContentMetaData; import org.molgenis.ontology.sorta.service.SortaService; import org.molgenis.security.core.runas.RunAsSystemProxy; import org.molgenis.ui.menu.MenuReaderService; @@ -19,7 +19,7 @@ import static com.google.common.collect.Lists.newArrayList; import static java.util.Objects.requireNonNull; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.SCORE; import static org.molgenis.util.ApplicationContextProvider.getApplicationContext; public class SortaJobProcessor @@ -64,31 +64,31 @@ public void process() progress.setProgressMax((int) maxCount); // FIXME get rid of getApplicationContext reference - MatchingTaskContentEntityMetaData matchingTaskContentEntityMetaData = getApplicationContext() - .getBean(MatchingTaskContentEntityMetaData.class); + MatchingTaskContentMetaData matchingTaskContentMetaData = getApplicationContext() + .getBean(MatchingTaskContentMetaData.class); // Match input terms with code List entitiesToAdd = newArrayList(); dataService.findAll(inputRepositoryName).forEach(inputRow -> { - Entity resultEntity = new DynamicEntity(matchingTaskContentEntityMetaData); - resultEntity.set(MatchingTaskContentEntityMetaData.INPUT_TERM, inputRow); - resultEntity.set(MatchingTaskContentEntityMetaData.IDENTIFIER, idGenerator.generateId()); - resultEntity.set(MatchingTaskContentEntityMetaData.VALIDATED, false); + Entity resultEntity = new DynamicEntity(matchingTaskContentMetaData); + resultEntity.set(MatchingTaskContentMetaData.INPUT_TERM, inputRow); + resultEntity.set(MatchingTaskContentMetaData.IDENTIFIER, idGenerator.generateId()); + resultEntity.set(MatchingTaskContentMetaData.VALIDATED, false); entitiesToAdd.add(resultEntity); Iterable ontologyTermEntities = sortaService.findOntologyTermEntities(ontologyIri, inputRow); if (Iterables.size(ontologyTermEntities) > 0) { Entity firstMatchedOntologyTerm = Iterables - .getFirst(ontologyTermEntities, new DynamicEntity(matchingTaskContentEntityMetaData)); - resultEntity.set(MatchingTaskContentEntityMetaData.MATCHED_TERM, + .getFirst(ontologyTermEntities, new DynamicEntity(matchingTaskContentMetaData)); + resultEntity.set(MatchingTaskContentMetaData.MATCHED_TERM, firstMatchedOntologyTerm.get(OntologyTermMetaData.ONTOLOGY_TERM_IRI)); - resultEntity.set(MatchingTaskContentEntityMetaData.SCORE, firstMatchedOntologyTerm.get(SCORE)); + resultEntity.set(MatchingTaskContentMetaData.SCORE, firstMatchedOntologyTerm.get(SCORE)); } else { - resultEntity.set(MatchingTaskContentEntityMetaData.SCORE, 0); + resultEntity.set(MatchingTaskContentMetaData.SCORE, 0); } // Add entity in batch diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentEntityMetaData.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentMetaData.java similarity index 91% rename from molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentEntityMetaData.java rename to molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentMetaData.java index 28a6b914180..431e75c0a1e 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentEntityMetaData.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/MatchingTaskContentMetaData.java @@ -13,7 +13,7 @@ import static org.molgenis.ontology.core.model.OntologyPackage.PACKAGE_ONTOLOGY; @Component -public class MatchingTaskContentEntityMetaData extends SystemEntityMetaData +public class MatchingTaskContentMetaData extends SystemEntityMetaData { public static final String SIMPLE_NAME = "MatchingTaskContent"; public static final String MATCHING_TASK_CONTENT = PACKAGE_ONTOLOGY + PACKAGE_SEPARATOR + SIMPLE_NAME; @@ -27,7 +27,7 @@ public class MatchingTaskContentEntityMetaData extends SystemEntityMetaData private final OntologyPackage ontologyPackage; @Autowired - MatchingTaskContentEntityMetaData(OntologyPackage ontologyPackage) + MatchingTaskContentMetaData(OntologyPackage ontologyPackage) { super(SIMPLE_NAME, PACKAGE_ONTOLOGY); this.ontologyPackage = requireNonNull(ontologyPackage); @@ -36,6 +36,7 @@ public class MatchingTaskContentEntityMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Matching task content"); setPackage(ontologyPackage); setAbstract(true); diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitEntityMetaData.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitMetaData.java similarity index 88% rename from molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitEntityMetaData.java rename to molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitMetaData.java index b67e51346c5..3ff07c3a53d 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitEntityMetaData.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/OntologyTermHitMetaData.java @@ -12,7 +12,7 @@ import static org.molgenis.ontology.core.model.OntologyPackage.PACKAGE_ONTOLOGY; @Component -public class OntologyTermHitEntityMetaData extends SystemEntityMetaData +public class OntologyTermHitMetaData extends SystemEntityMetaData { private static final String SIMPLE_NAME = "OntologyTermHit"; public static final String ONTOLOGY_TERM_HIT = PACKAGE_ONTOLOGY + PACKAGE_SEPARATOR + SIMPLE_NAME; @@ -24,7 +24,7 @@ public class OntologyTermHitEntityMetaData extends SystemEntityMetaData private final OntologyPackage ontologyPackage; @Autowired - public OntologyTermHitEntityMetaData(OntologyPackage ontologyPackage) + public OntologyTermHitMetaData(OntologyPackage ontologyPackage) { super(SIMPLE_NAME, PACKAGE_ONTOLOGY); this.ontologyPackage = requireNonNull(ontologyPackage); @@ -33,6 +33,7 @@ public OntologyTermHitEntityMetaData(OntologyPackage ontologyPackage) @Override public void init() { + setLabel("Ontology term hit"); setPackage(ontologyPackage); addAttribute(ID, ROLE_ID).setAuto(true); diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/SortaJobExecutionMetaData.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/SortaJobExecutionMetaData.java index 0bed4fac7ff..c1a1432f4d4 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/SortaJobExecutionMetaData.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/meta/SortaJobExecutionMetaData.java @@ -40,6 +40,7 @@ public class SortaJobExecutionMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("SORTA job execution"); setPackage(ontologyPackage); setExtends(jobExecutionMetaData); diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/service/impl/SortaServiceImpl.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/service/impl/SortaServiceImpl.java index 0ab66520b6e..c9dbf497f7a 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/service/impl/SortaServiceImpl.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/service/impl/SortaServiceImpl.java @@ -14,7 +14,7 @@ import org.molgenis.ontology.core.meta.*; import org.molgenis.ontology.roc.InformationContentService; import org.molgenis.ontology.sorta.bean.OntologyTermHitEntity; -import org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData; +import org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData; import org.molgenis.ontology.sorta.service.SortaService; import org.springframework.beans.factory.annotation.Autowired; @@ -27,8 +27,8 @@ import static org.molgenis.ontology.core.meta.OntologyMetaData.ONTOLOGY; import static org.molgenis.ontology.core.meta.OntologyTermDynamicAnnotationMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION; import static org.molgenis.ontology.core.meta.OntologyTermMetaData.ONTOLOGY_TERM; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.COMBINED_SCORE; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.COMBINED_SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.SCORE; public class SortaServiceImpl implements SortaService { @@ -49,17 +49,17 @@ public class SortaServiceImpl implements SortaService private final DataService dataService; private final InformationContentService informationContentService; - private final OntologyTermHitEntityMetaData ontologyTermHitEntityMetaData; + private final OntologyTermHitMetaData ontologyTermHitMetaData; private final OntologyTermSynonymFactory ontologyTermSynonymFactory; @Autowired public SortaServiceImpl(DataService dataService, InformationContentService informationContentService, - OntologyTermHitEntityMetaData ontologyTermHitEntityMetaData, + OntologyTermHitMetaData ontologyTermHitMetaData, OntologyTermSynonymFactory ontologyTermSynonymFactory) { this.dataService = requireNonNull(dataService); this.informationContentService = requireNonNull(informationContentService); - this.ontologyTermHitEntityMetaData = requireNonNull(ontologyTermHitEntityMetaData); + this.ontologyTermHitMetaData = requireNonNull(ontologyTermHitMetaData); this.ontologyTermSynonymFactory = requireNonNull(ontologyTermSynonymFactory); } @@ -239,7 +239,7 @@ Entity addLexicalScoreToMatchedEntity(Entity inputEntity, Entity ontologyTerm, S } } } - OntologyTermHitEntity mapEntity = new OntologyTermHitEntity(ontologyTerm, ontologyTermHitEntityMetaData); + OntologyTermHitEntity mapEntity = new OntologyTermHitEntity(ontologyTerm, ontologyTermHitMetaData); mapEntity.set(SCORE, maxNgramScore); mapEntity.set(COMBINED_SCORE, maxNgramIDFScore); return mapEntity; @@ -256,7 +256,7 @@ Entity addLexicalScoreToMatchedEntity(Entity inputEntity, Entity ontologyTerm, S */ private Entity calculateNGromOTAnnotations(Entity inputEntity, Entity ontologyTermEntity) { - OntologyTermHitEntity mapEntity = new OntologyTermHitEntity(ontologyTermEntity, ontologyTermHitEntityMetaData); + OntologyTermHitEntity mapEntity = new OntologyTermHitEntity(ontologyTermEntity, ontologyTermHitMetaData); for (Entity annotationEntity : ontologyTermEntity .getEntities(OntologyTermMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION)) { diff --git a/molgenis-ontology/src/test/java/org/molgenis/ontology/sorta/SortaServiceImplTest.java b/molgenis-ontology/src/test/java/org/molgenis/ontology/sorta/SortaServiceImplTest.java index e628861e64d..12c3ca3c69f 100644 --- a/molgenis-ontology/src/test/java/org/molgenis/ontology/sorta/SortaServiceImplTest.java +++ b/molgenis-ontology/src/test/java/org/molgenis/ontology/sorta/SortaServiceImplTest.java @@ -9,7 +9,7 @@ import org.molgenis.data.support.QueryImpl; import org.molgenis.ontology.core.meta.*; import org.molgenis.ontology.roc.InformationContentService; -import org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData; +import org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData; import org.molgenis.ontology.sorta.service.impl.SortaServiceImpl; import org.molgenis.test.data.AbstractMolgenisSpringTest; import org.springframework.beans.factory.annotation.Autowired; @@ -36,7 +36,7 @@ import static org.molgenis.ontology.core.meta.OntologyMetaData.ONTOLOGY; import static org.molgenis.ontology.core.meta.OntologyTermDynamicAnnotationMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION; import static org.molgenis.ontology.core.meta.OntologyTermMetaData.ONTOLOGY_TERM; -import static org.molgenis.ontology.sorta.meta.OntologyTermHitEntityMetaData.COMBINED_SCORE; +import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.COMBINED_SCORE; import static org.testng.Assert.assertEquals; @ContextConfiguration(classes = { SortaServiceImplTest.Config.class }) @@ -340,7 +340,7 @@ public void getOntologyTermEntity() public static class Config { @Autowired - public OntologyTermHitEntityMetaData ontologyTermHitEntityMetaData; + public OntologyTermHitMetaData ontologyTermHitMetaData; @Autowired private OntologyTermSynonymFactory ontologyTermSynonymFactory; @@ -360,7 +360,7 @@ public InformationContentService informationContentService() @Bean public SortaServiceImpl sortaServiceImpl() { - return new SortaServiceImpl(dataService(), informationContentService(), ontologyTermHitEntityMetaData, + return new SortaServiceImpl(dataService(), informationContentService(), ontologyTermHitMetaData, ontologyTermSynonymFactory); } } diff --git a/molgenis-questionnaires/src/main/java/org/molgenis/questionnaires/QuestionnaireMetaData.java b/molgenis-questionnaires/src/main/java/org/molgenis/questionnaires/QuestionnaireMetaData.java index 0cc3b88489f..d30b7bb3b4f 100644 --- a/molgenis-questionnaires/src/main/java/org/molgenis/questionnaires/QuestionnaireMetaData.java +++ b/molgenis-questionnaires/src/main/java/org/molgenis/questionnaires/QuestionnaireMetaData.java @@ -36,6 +36,7 @@ public class QuestionnaireMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Questionnaire"); setAbstract(true); setExtends(ownedEntityMetaData); diff --git a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptMetaData.java b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptMetaData.java index 18a0c256e08..22a43497331 100644 --- a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptMetaData.java +++ b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptMetaData.java @@ -45,6 +45,7 @@ public class ScriptMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Script"); setPackage(scriptPackage); addAttribute(NAME, ROLE_ID).setNillable(false).setLabel("Name"); diff --git a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptParameterMetaData.java b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptParameterMetaData.java index 5558836f905..cdfef9a5da0 100644 --- a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptParameterMetaData.java +++ b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptParameterMetaData.java @@ -29,6 +29,7 @@ public class ScriptParameterMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Script parameter"); setPackage(scriptPackage); addAttribute(NAME, ROLE_ID).setNillable(false).setLabel("Name label"); diff --git a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptTypeMetaData.java b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptTypeMetaData.java index e416cdcb79e..079ac65a43b 100644 --- a/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptTypeMetaData.java +++ b/molgenis-scripts-core/src/main/java/org/molgenis/script/ScriptTypeMetaData.java @@ -29,6 +29,7 @@ public class ScriptTypeMetaData extends SystemEntityMetaData @Override public void init() { + setLabel("Script type"); setPackage(scriptPackage); addAttribute(NAME, ROLE_ID).setNillable(false); diff --git a/molgenis-security/src/main/java/org/molgenis/security/owned/OwnedEntityMetaData.java b/molgenis-security/src/main/java/org/molgenis/security/owned/OwnedEntityMetaData.java index c1690547021..35606f314db 100644 --- a/molgenis-security/src/main/java/org/molgenis/security/owned/OwnedEntityMetaData.java +++ b/molgenis-security/src/main/java/org/molgenis/security/owned/OwnedEntityMetaData.java @@ -38,6 +38,7 @@ public OwnedEntityMetaData(SecurityPackage securityPackage) @Override public void init() { + setLabel("Owned"); setPackage(securityPackage); setAbstract(true); From 8ef50425b23fba6252b1512c8d6bb0796da65e17 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 09:32:01 +0200 Subject: [PATCH 18/49] Consistency: use separate attribute names for EMX entities instead of EntityMetaDataMetaData field names (similar to e.g. EMX tags and attributes) --- .../data/importer/EmxMetaDataParser.java | 204 ++++++++++-------- .../importer/IntermediateParseResults.java | 2 +- 2 files changed, 116 insertions(+), 90 deletions(-) diff --git a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java index db7a7ef663d..67c73454b4e 100644 --- a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java +++ b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java @@ -37,10 +37,7 @@ import static org.molgenis.data.importer.MyEntitiesValidationReport.AttributeState.*; import static org.molgenis.data.meta.DefaultPackage.PACKAGE_DEFAULT; import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.*; -import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.DESCRIPTION; -import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.LABEL; -import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.TAGS; -import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ENTITY_META_DATA; import static org.molgenis.data.meta.model.Package.PACKAGE_SEPARATOR; import static org.molgenis.data.meta.model.TagMetaData.TAG; import static org.molgenis.data.semantic.SemanticTag.asTag; @@ -57,44 +54,54 @@ public class EmxMetaDataParser implements MetaDataParser { // Column names in the attributes sheet - public static final String ENTITY = "entity"; - public static final String ID_ATTRIBUTE = "idAttribute"; - public static final String LOOKUP_ATTRIBUTE = "lookupAttribute"; - public static final String LABEL_ATTRIBUTE = "labelAttribute"; - public static final String PART_OF_ATTRIBUTE = "partOfAttribute"; + private static final String ENTITY = "entity"; + private static final String ID_ATTRIBUTE = "idAttribute"; + private static final String LOOKUP_ATTRIBUTE = "lookupAttribute"; + private static final String LABEL_ATTRIBUTE = "labelAttribute"; + private static final String PART_OF_ATTRIBUTE = "partOfAttribute"; // Table names in the source - public static final String EMX_PACKAGES = "packages"; - public static final String EMX_ENTITIES = "entities"; - public static final String EMX_ATTRIBUTES = "attributes"; - public static final String EMX_TAGS = "tags"; - public static final String EMX_LANGUAGES = "languages"; - public static final String EMX_I18NSTRINGS = "i18nstrings"; + static final String EMX_PACKAGES = "packages"; + static final String EMX_ENTITIES = "entities"; + static final String EMX_ATTRIBUTES = "attributes"; + static final String EMX_TAGS = "tags"; + static final String EMX_LANGUAGES = "languages"; + static final String EMX_I18NSTRINGS = "i18nstrings"; + + // Column names in the entities sheet + private static final String EMX_ENTITIES_NAME = "name"; + private static final String EMX_ENTITIES_PACKAGE = "package"; + private static final String EMX_ENTITIES_LABEL = "label"; + private static final String EMX_ENTITIES_DESCRIPTION = "description"; + private static final String EMX_ENTITIES_ABSTRACT = "abstract"; + private static final String EMX_ENTITIES_EXTENDS = "extends"; + private static final String EMX_ENTITIES_BACKEND = "backend"; + private static final String EMX_ENTITIES_TAGS = "tags"; // Column names in the tag sheet - public static final String EMX_TAG_IDENTIFIER = "identifier"; - public static final String EMX_TAG_OBJECT_IRI = "objectIRI"; - public static final String EMX_TAG_LABEL = "label"; - public static final String EMX_TAG_RELATION_LABEL = "relationLabel"; - public static final String EMX_TAG_CODE_SYSTEM = "codeSystem"; - public static final String EMX_TAG_RELATION_IRI = "relationIRI"; + static final String EMX_TAG_IDENTIFIER = "identifier"; + static final String EMX_TAG_OBJECT_IRI = "objectIRI"; + static final String EMX_TAG_LABEL = "label"; + static final String EMX_TAG_RELATION_LABEL = "relationLabel"; + static final String EMX_TAG_CODE_SYSTEM = "codeSystem"; + static final String EMX_TAG_RELATION_IRI = "relationIRI"; // Column names in the language sheet - public static final String EMX_LANGUAGE_CODE = "code"; - public static final String EMX_LANGUAGE_NAME = "name"; + private static final String EMX_LANGUAGE_CODE = "code"; + private static final String EMX_LANGUAGE_NAME = "name"; // Column names in the i18nstring sheet - public static final String EMX_I18N_STRING_MSGID = "msgid"; - public static final String EMX_I18N_STRING_DESCRIPTION = "description"; + private static final String EMX_I18N_STRING_MSGID = "msgid"; + private static final String EMX_I18N_STRING_DESCRIPTION = "description"; // Column names in the package sheet - public static final String EMX_PACKAGE_NAME = "name"; - public static final String EMX_PACKAGE_DESCRIPTION = "description"; - public static final String EMX_PACKAGE_PARENT = "parent"; - public static final String EMX_PACKAGE_TAGS = "tags"; - public static final String EMX_PACKAGE_LABEL = "label"; + private static final String EMX_PACKAGE_NAME = "name"; + private static final String EMX_PACKAGE_DESCRIPTION = "description"; + private static final String EMX_PACKAGE_PARENT = "parent"; + private static final String EMX_PACKAGE_TAGS = "tags"; + private static final String EMX_PACKAGE_LABEL = "label"; - public static Map EMX_NAME_TO_REPO_NAME_MAP = newHashMap(); + private static final Map EMX_NAME_TO_REPO_NAME_MAP = newHashMap(); static { @@ -106,12 +113,13 @@ public class EmxMetaDataParser implements MetaDataParser EMX_NAME_TO_REPO_NAME_MAP.put(EMX_I18NSTRINGS, I18N_STRING); } - public static final List SUPPORTED_ENTITY_ATTRIBUTES = Arrays - .asList(EntityMetaDataMetaData.LABEL.toLowerCase(), EntityMetaDataMetaData.DESCRIPTION.toLowerCase(), - "name", ABSTRACT.toLowerCase(), EXTENDS.toLowerCase(), "package", EntityMetaDataMetaData.TAGS, - BACKEND); + private static final List EMX_ENTITIES_ALLOWED_ATTRS = Arrays + .asList(EMX_ENTITIES_NAME.toLowerCase(), EMX_ENTITIES_PACKAGE.toLowerCase(), + EMX_ENTITIES_LABEL.toLowerCase(), EMX_ENTITIES_DESCRIPTION, EMX_ENTITIES_ABSTRACT.toLowerCase(), + EMX_ENTITIES_EXTENDS.toLowerCase(), EMX_ENTITIES_BACKEND.toLowerCase(), + EMX_ENTITIES_TAGS.toLowerCase()); - public static final List SUPPORTED_ATTRIBUTE_ATTRIBUTES = Arrays + private static final List SUPPORTED_ATTRIBUTE_ATTRIBUTES = Arrays .asList(AGGREGATEABLE.toLowerCase(), DATA_TYPE.toLowerCase(), DESCRIPTION.toLowerCase(), ENTITY.toLowerCase(), ENUM_OPTIONS.toLowerCase(), ID_ATTRIBUTE.toLowerCase(), LABEL.toLowerCase(), LABEL_ATTRIBUTE.toLowerCase(), LOOKUP_ATTRIBUTE.toLowerCase(), NAME, NILLABLE.toLowerCase(), @@ -120,7 +128,7 @@ public class EmxMetaDataParser implements MetaDataParser TAGS.toLowerCase(), EXPRESSION.toLowerCase(), VALIDATION_EXPRESSION.toLowerCase(), DEFAULT_VALUE.toLowerCase()); - public static final String AUTO = "auto"; + private static final String AUTO = "auto"; private final DataService dataService; private final PackageFactory packageFactory; @@ -323,7 +331,7 @@ private void parsePackagesSheet(Repository repo, IntermediateParseResult if (parentName != null) { if (!name.toLowerCase().startsWith(parentName.toLowerCase())) throw new MolgenisDataException( - "Inconsistent package structure. Package: '" + name + "', parent: '" + parentName + "'"); + "Inconsistent package structure. Package: '" + name + "', parent: '" + parentName + '\''); String simpleName = name.substring(parentName.length() + 1); // subpackage_package package_.setSimpleName(simpleName); @@ -365,7 +373,7 @@ private List parsePackageTags(IntermediateParseResults intermediateResults, } else { - throw new IllegalArgumentException("Unknown tag '" + tagIdentifier + "'"); + throw new IllegalArgumentException("Unknown tag '" + tagIdentifier + '\''); } } @@ -379,7 +387,7 @@ private List parsePackageTags(IntermediateParseResults intermediateResults, * @param tagEntity * @return */ - public Tag entityToTag(String id, Entity tagEntity) + private Tag entityToTag(String id, Entity tagEntity) { Tag tag = tagFactory.create(id); tag.setObjectIri(tagEntity.getString(EMX_TAG_OBJECT_IRI)); @@ -403,9 +411,9 @@ private void parseEntitiesSheet(Repository entitiesRepo, IntermediatePar { for (AttributeMetaData attr : entitiesRepo.getEntityMetaData().getAtomicAttributes()) { - if (!SUPPORTED_ENTITY_ATTRIBUTES.contains(attr.getName().toLowerCase()) && !(isI18n(attr.getName()) && ( - attr.getName().startsWith(EntityMetaDataMetaData.DESCRIPTION) || attr.getName() - .startsWith(EntityMetaDataMetaData.LABEL)))) + if (!EMX_ENTITIES_ALLOWED_ATTRS.contains(attr.getName().toLowerCase()) && !(isI18n(attr.getName()) && ( + attr.getName().startsWith(EMX_ENTITIES_DESCRIPTION) || attr.getName() + .startsWith(EMX_ENTITIES_LABEL)))) { throw new IllegalArgumentException("Unsupported entity metadata: entities." + attr.getName()); } @@ -415,119 +423,137 @@ private void parseEntitiesSheet(Repository entitiesRepo, IntermediatePar for (Entity entity : entitiesRepo) { i++; - String entityName = entity.getString("name"); + String emxEntityName = entity.getString(EMX_ENTITIES_NAME); + String emxEntityPackage = entity.getString(EMX_ENTITIES_PACKAGE); + String emxEntityLabel = entity.getString(EMX_ENTITIES_LABEL); + String emxEntityDescription = entity.getString(EMX_ENTITIES_DESCRIPTION); + String emxEntityAbstract = entity.getString(EMX_ENTITIES_ABSTRACT); + String emxEntityExtends = entity.getString(EMX_ENTITIES_EXTENDS); + String emxEntityBackend = entity.getString(EMX_ENTITIES_BACKEND); + String emxEntityTags = entity.getString(EMX_ENTITIES_TAGS); // required - if (entityName == null) throw new IllegalArgumentException("entity.name is missing on line " + i); + if (emxEntityName == null) + { + throw new IllegalArgumentException("entity.name is missing on line " + i); + } - String packageName = entity.getString(PACKAGE); - if (packageName != null && !PACKAGE_DEFAULT.equals(packageName)) + String entityName; + if (emxEntityPackage != null && !PACKAGE_DEFAULT.equals(emxEntityPackage)) + { + entityName = emxEntityPackage + PACKAGE_SEPARATOR + emxEntityName; + } + else { - entityName = packageName + PACKAGE_SEPARATOR + entityName; + entityName = emxEntityName; } - EntityMetaData md = intermediateResults.getEntityMetaData(entityName); - if (md == null) + EntityMetaData entityMeta = intermediateResults.getEntityMetaData(entityName); + if (entityMeta == null) { - md = intermediateResults.addEntityMetaData(entityName); + entityMeta = intermediateResults.addEntityMetaData(entityName); } if (dataService != null) { - String backend = entity.getString(BACKEND); - if (backend != null) + if (emxEntityBackend != null) { - if (dataService.getMeta().getBackend(backend) == null) + if (dataService.getMeta().getBackend(emxEntityBackend) == null) { - throw new MolgenisDataException("Unknown backend '" + backend + "'"); + throw new MolgenisDataException("Unknown backend '" + emxEntityBackend + '\''); } } else { - backend = dataService.getMeta().getDefaultBackend().getName(); + emxEntityBackend = dataService.getMeta().getDefaultBackend().getName(); } - md.setBackend(backend); + entityMeta.setBackend(emxEntityBackend); } - if (packageName != null) + if (emxEntityPackage != null) { - Package p = intermediateResults.getPackage(packageName); + Package p = intermediateResults.getPackage(emxEntityPackage); if (p == null) { throw new MolgenisDataException( - "Unknown package: '" + packageName + "' for entity '" + entity.getString("name") + "Unknown package: '" + emxEntityPackage + "' for entity '" + emxEntityName + "'. Please specify the package on the " + EMX_PACKAGES + " sheet and use the fully qualified package and entity names."); } - md.setPackage(p); + entityMeta.setPackage(p); } - md.setLabel(entity.getString(EntityMetaDataMetaData.LABEL)); - md.setDescription(entity.getString(EntityMetaDataMetaData.DESCRIPTION)); + entityMeta.setLabel(emxEntityLabel); + + entityMeta.setDescription(emxEntityDescription); for (String attributeName : entity.getAttributeNames()) { if (isI18n(attributeName)) { - if (attributeName.startsWith(EntityMetaDataMetaData.DESCRIPTION)) + if (attributeName.startsWith(EMX_ENTITIES_DESCRIPTION)) { String description = entity.getString(attributeName); if (description != null) { String languageCode = getLanguageCode(attributeName); - md.setDescription(languageCode, description); + entityMeta.setDescription(languageCode, description); } } - else if (attributeName.startsWith(EntityMetaDataMetaData.LABEL)) + else if (attributeName.startsWith(EMX_ENTITIES_LABEL)) { String label = entity.getString(attributeName); if (label != null) { String languageCode = getLanguageCode(attributeName); - md.setLabel(languageCode, label); + entityMeta.setLabel(languageCode, label); } } } } - String abstractString = entity.getString(ABSTRACT); - if (abstractString != null) md.setAbstract(parseBoolean(abstractString, i, ABSTRACT)); - List tagIds = toList(entity.get(TAGS)); + if (emxEntityAbstract != null) + { + entityMeta.setAbstract(parseBoolean(emxEntityAbstract, i, EMX_ENTITIES_ABSTRACT)); + } + + List tagIds = toList(emxEntityTags); - String extendsEntityName = entity.getString(EXTENDS); - if (extendsEntityName != null) + if (emxEntityExtends != null) { EntityMetaData extendsEntityMeta = null; - if (intermediateResults.knowsEntity(extendsEntityName)) + if (intermediateResults.knowsEntity(emxEntityExtends)) { - extendsEntityMeta = intermediateResults.getEntityMetaData(extendsEntityName); + extendsEntityMeta = intermediateResults.getEntityMetaData(emxEntityExtends); } else { if (dataService != null) - extendsEntityMeta = dataService.getMeta().getEntityMetaData(extendsEntityName); + { + extendsEntityMeta = dataService.getMeta().getEntityMetaData(emxEntityExtends); + } } if (extendsEntityMeta == null) { throw new MolgenisDataException( - "Missing super entity " + extendsEntityName + " for entity " + entityName + " on line " - + i); + "Missing super entity " + emxEntityExtends + " for entity " + emxEntityName + + " on line " + i); } - md.setExtends(extendsEntityMeta); + entityMeta.setExtends(extendsEntityMeta); } if (tagIds != null && !tagIds.isEmpty()) { - importTags(intermediateResults, entityName, md, tagIds); + importTags(intermediateResults, emxEntityName, entityMeta, tagIds); } } } } - private void importTags(IntermediateParseResults intermediateResults, String entityName, EntityMetaData md, - List tagIds) + private static void importTags(IntermediateParseResults intermediateResults, String emxEntityName, + EntityMetaData md, List tagIds) { for (String tagId : tagIds) { @@ -535,10 +561,10 @@ private void importTags(IntermediateParseResults intermediateResults, String ent if (tagEntity == null) { throw new MolgenisDataException( - "Unknown tag: " + tagId + " for entity [" + entityName + "]). Please specify on the " + EMX_TAGS - + " sheet."); + "Unknown tag: " + tagId + " for entity [" + emxEntityName + "]). Please specify on the " + + EMX_TAGS + " sheet."); } - intermediateResults.addEntityTag(entityName, asTag(md, tagEntity)); + intermediateResults.addEntityTag(asTag(md, tagEntity)); } } @@ -548,7 +574,7 @@ private void importTags(IntermediateParseResults intermediateResults, String ent * @param packageRepo * @return */ - private List resolvePackages(Repository packageRepo) + private static List resolvePackages(Repository packageRepo) { List resolved = new ArrayList<>(); if ((packageRepo == null) || Iterables.isEmpty(packageRepo)) return resolved; @@ -1031,11 +1057,11 @@ private void reiterateToMapRefEntity(Repository attributeRepo, Intermedi * @param defaultPackageName * @return */ - private List putEntitiesInDefaultPackage(IntermediateParseResults intermediateResults, + private static List putEntitiesInDefaultPackage(IntermediateParseResults intermediateResults, String defaultPackageName) { Package p = intermediateResults.getPackage(defaultPackageName); - if (p == null) throw new IllegalArgumentException("Unknown package '" + defaultPackageName + "'"); + if (p == null) throw new IllegalArgumentException("Unknown package '" + defaultPackageName + '\''); List entities = newArrayList(); for (EntityMetaData entityMetaData : intermediateResults.getEntities()) @@ -1046,7 +1072,7 @@ private List putEntitiesInDefaultPackage(IntermediateParseResult return entities; } - private ImmutableMap getEntityMetaDataFromDataService(DataService dataService, + private static ImmutableMap getEntityMetaDataFromDataService(DataService dataService, Iterable emxEntityNames) { ImmutableMap.Builder builder = builder(); @@ -1116,7 +1142,7 @@ else if ((!EntityUtils.equals(emd, allEntityMetaDataMap.get(emd.getName()))) * @return true or false * @throws IllegalArgumentException if the given boolean string value is not one of [true, false] (case insensitive) */ - private boolean parseBoolean(String booleanString, int rowIndex, String columnName) + private static boolean parseBoolean(String booleanString, int rowIndex, String columnName) { if (booleanString.equalsIgnoreCase(TRUE.toString())) return true; else if (booleanString.equalsIgnoreCase(FALSE.toString())) return false; diff --git a/molgenis-data-import/src/main/java/org/molgenis/data/importer/IntermediateParseResults.java b/molgenis-data-import/src/main/java/org/molgenis/data/importer/IntermediateParseResults.java index 9c6bd0e4758..cfe9da8b4ed 100644 --- a/molgenis-data-import/src/main/java/org/molgenis/data/importer/IntermediateParseResults.java +++ b/molgenis-data-import/src/main/java/org/molgenis/data/importer/IntermediateParseResults.java @@ -84,7 +84,7 @@ public Entity getTagEntity(String tagIdentifier) return tags.get(tagIdentifier); } - public void addEntityTag(String entityName, SemanticTag tag) + public void addEntityTag(SemanticTag tag) { entityTags.add(tag); } From ad037a51c87ea276c2ca032a857b6cd1d195632f Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 10:37:06 +0200 Subject: [PATCH 19/49] When translating foreign key exceptions distinguish between unknown values and values that can't be removed --- .../PostgreSqlExceptionTranslator.java | 16 ++++++- .../PostgreSqlExceptionTranslatorTest.java | 42 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java index d635b267870..3886c60f51f 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java @@ -201,8 +201,22 @@ MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlExcep throw new RuntimeException("Error translating exception", pSqlException); } String value = m.group(1); + + String constraintViolationMessageTemplate; + if (detailMessage.contains("still referenced from")) + { + // ERROR: update or delete on table "x" violates foreign key constraint "y" on table "z" + // Detail: Key (k)=(v) is still referenced from table "x". + constraintViolationMessageTemplate = "Value '%s' for attribute '%s' is referenced by entity '%s'."; + } + else + { + // ERROR: insert or update on table "x" violates foreign key constraint "y" + // Detail: Key (k)=(v) is not present in table "z". + constraintViolationMessageTemplate = "Unknown xref value '%s' for attribute '%s' of entity '%s'."; + } ConstraintViolation constraintViolation = new ConstraintViolation( - format("Unknown xref value '%s' for attribute '%s' of entity '%s'.", value, colName, tableName), null); + format(constraintViolationMessageTemplate, value, colName, tableName), null); return new MolgenisValidationException(singleton(constraintViolation)); } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java index 51df0aa10d5..4133fd67f8e 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java @@ -29,6 +29,7 @@ public void translateNotNullViolation() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getMessage()) .thenReturn("null value in column \"mycolumn\" violates not-null constraint"); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateNotNullViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "The attribute 'mycolumn' of entity 'mytable' can not be null."); @@ -43,6 +44,7 @@ public void translateNotNullViolationBadMessage() when(serverErrorMessage.getSQLState()).thenReturn("23502"); when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getMessage()).thenReturn("xxxyyyzzzz"); + //noinspection ThrowableResultOfMethodCallIgnored postgreSqlExceptionTranslator.translateNotNullViolation(new PSQLException(serverErrorMessage)); } @@ -55,11 +57,43 @@ public void translateForeignKeyViolation() when(serverErrorMessage.getSQLState()).thenReturn("23503"); when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("... (mycolumn) ... (myvalue) ..."); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Unknown xref value 'myvalue' for attribute 'mycolumn' of entity 'mytable'."); } + @Test + public void translateForeignKeyViolationNotPresent() + { + DataSource dataSource = mock(DataSource.class); + PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); + ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); + when(serverErrorMessage.getSQLState()).thenReturn("23503"); + when(serverErrorMessage.getTable()).thenReturn("mytable"); + when(serverErrorMessage.getDetail()).thenReturn("Key (mycolumn)=(myvalue) is not present in table \"mytable\""); + //noinspection ThrowableResultOfMethodCallIgnored + MolgenisValidationException e = postgreSqlExceptionTranslator + .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); + assertEquals(e.getMessage(), "Unknown xref value 'myvalue' for attribute 'mycolumn' of entity 'mytable'."); + } + + @Test + public void translateForeignKeyViolationStillReferenced() + { + DataSource dataSource = mock(DataSource.class); + PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); + ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); + when(serverErrorMessage.getSQLState()).thenReturn("23503"); + when(serverErrorMessage.getTable()).thenReturn("mytable"); + when(serverErrorMessage.getDetail()) + .thenReturn("Key (mycolumn)=(myvalue) is still referenced from table \"mytable\""); + //noinspection ThrowableResultOfMethodCallIgnored + MolgenisValidationException e = postgreSqlExceptionTranslator + .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); + assertEquals(e.getMessage(), "Value 'myvalue' for attribute 'mycolumn' is referenced by entity 'mytable'."); + } + @Test(expectedExceptions = RuntimeException.class) public void translateForeignKeyViolationBadMessage() { @@ -69,6 +103,7 @@ public void translateForeignKeyViolationBadMessage() when(serverErrorMessage.getSQLState()).thenReturn("23503"); when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("xxxyyyyzzzz"); + //noinspection ThrowableResultOfMethodCallIgnored postgreSqlExceptionTranslator.translateForeignKeyViolation(new PSQLException(serverErrorMessage)); } @@ -81,6 +116,7 @@ public void translateUniqueKeyViolation() when(serverErrorMessage.getSQLState()).thenReturn("23505"); when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("Key (mycolumn)=(myvalue) already exists."); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateUniqueKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), @@ -96,6 +132,7 @@ public void translateUniqueKeyViolationBadMessage() when(serverErrorMessage.getSQLState()).thenReturn("23505"); when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("xxxyyyyzzz"); + //noinspection ThrowableResultOfMethodCallIgnored postgreSqlExceptionTranslator.translateUniqueKeyViolation(new PSQLException(serverErrorMessage)); } @@ -106,6 +143,7 @@ public void translateInvalidIntegerExceptionInteger() PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for integer: \"str1\""); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [INT or LONG]."); @@ -118,6 +156,7 @@ public void translateInvalidIntegerExceptionBoolean() PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type boolean: \"str1\""); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [BOOL]."); @@ -130,6 +169,7 @@ public void translateInvalidIntegerExceptionDouble() PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type double precision: \"str1\""); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DECIMAL]."); @@ -142,6 +182,7 @@ public void translateInvalidIntegerExceptionDate() PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type date: \"str1\""); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DATE]."); @@ -154,6 +195,7 @@ public void translateInvalidIntegerExceptionDateTime() PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type timestamp: \"str1\""); + //noinspection ThrowableResultOfMethodCallIgnored MolgenisValidationException e = postgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DATE_TIME]."); From c7e879c074a9441f01cceb9240166ad6b6109b97 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 10:38:53 +0200 Subject: [PATCH 20/49] Refactor: make methods static where possible --- .../PostgreSqlExceptionTranslator.java | 14 +++++----- .../PostgreSqlExceptionTranslatorTest.java | 26 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java index 3886c60f51f..ee4d65ed8c1 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java @@ -39,7 +39,7 @@ protected DataAccessException doTranslate(String task, String sql, SQLException return doTranslate(dataAccessException); } - private MolgenisDataException doTranslate(DataAccessException dataAccessException) + private static MolgenisDataException doTranslate(DataAccessException dataAccessException) { Throwable cause = dataAccessException.getCause(); if (!(cause instanceof PSQLException)) @@ -56,7 +56,7 @@ private MolgenisDataException doTranslate(DataAccessException dataAccessExceptio return molgenisDataException; } - private MolgenisDataException doTranslate(SQLException sqlException) + private static MolgenisDataException doTranslate(SQLException sqlException) { if (!(sqlException instanceof PSQLException)) { @@ -73,7 +73,7 @@ private MolgenisDataException doTranslate(SQLException sqlException) return molgenisDataException; } - private MolgenisDataException doTranslate(PSQLException pSqlException) + private static MolgenisDataException doTranslate(PSQLException pSqlException) { switch (pSqlException.getSQLState()) { @@ -97,7 +97,7 @@ private MolgenisDataException doTranslate(PSQLException pSqlException) * @param pSqlException PostgreSQL exception * @return translated validation exception */ - MolgenisValidationException translateInvalidIntegerException(PSQLException pSqlException) + static MolgenisValidationException translateInvalidIntegerException(PSQLException pSqlException) { ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); String message = serverErrorMessage.getMessage(); @@ -145,7 +145,7 @@ MolgenisValidationException translateInvalidIntegerException(PSQLException pSqlE * @param pSqlException PostgreSQL exception * @return translated validation exception */ - MolgenisValidationException translateNotNullViolation(PSQLException pSqlException) + static MolgenisValidationException translateNotNullViolation(PSQLException pSqlException) { ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); String tableName = serverErrorMessage.getTable(); @@ -185,7 +185,7 @@ MolgenisValidationException translateNotNullViolation(PSQLException pSqlExceptio * @param pSqlException PostgreSQL exception * @return translated validation exception */ - MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlException) + static MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlException) { ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); String tableName = serverErrorMessage.getTable(); @@ -226,7 +226,7 @@ MolgenisValidationException translateForeignKeyViolation(PSQLException pSqlExcep * @param pSqlException PostgreSQL exception * @return translated validation exception */ - MolgenisValidationException translateUniqueKeyViolation(PSQLException pSqlException) + static MolgenisValidationException translateUniqueKeyViolation(PSQLException pSqlException) { ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); String tableName = serverErrorMessage.getTable(); diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java index 4133fd67f8e..2f7acb70736 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java @@ -30,7 +30,7 @@ public void translateNotNullViolation() when(serverErrorMessage.getMessage()) .thenReturn("null value in column \"mycolumn\" violates not-null constraint"); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateNotNullViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "The attribute 'mycolumn' of entity 'mytable' can not be null."); } @@ -45,7 +45,7 @@ public void translateNotNullViolationBadMessage() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getMessage()).thenReturn("xxxyyyzzzz"); //noinspection ThrowableResultOfMethodCallIgnored - postgreSqlExceptionTranslator.translateNotNullViolation(new PSQLException(serverErrorMessage)); + PostgreSqlExceptionTranslator.translateNotNullViolation(new PSQLException(serverErrorMessage)); } @Test @@ -58,7 +58,7 @@ public void translateForeignKeyViolation() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("... (mycolumn) ... (myvalue) ..."); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Unknown xref value 'myvalue' for attribute 'mycolumn' of entity 'mytable'."); } @@ -73,7 +73,7 @@ public void translateForeignKeyViolationNotPresent() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("Key (mycolumn)=(myvalue) is not present in table \"mytable\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Unknown xref value 'myvalue' for attribute 'mycolumn' of entity 'mytable'."); } @@ -89,7 +89,7 @@ public void translateForeignKeyViolationStillReferenced() when(serverErrorMessage.getDetail()) .thenReturn("Key (mycolumn)=(myvalue) is still referenced from table \"mytable\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateForeignKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value 'myvalue' for attribute 'mycolumn' is referenced by entity 'mytable'."); } @@ -104,7 +104,7 @@ public void translateForeignKeyViolationBadMessage() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("xxxyyyyzzzz"); //noinspection ThrowableResultOfMethodCallIgnored - postgreSqlExceptionTranslator.translateForeignKeyViolation(new PSQLException(serverErrorMessage)); + PostgreSqlExceptionTranslator.translateForeignKeyViolation(new PSQLException(serverErrorMessage)); } @Test @@ -117,7 +117,7 @@ public void translateUniqueKeyViolation() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("Key (mycolumn)=(myvalue) already exists."); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateUniqueKeyViolation(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Duplicate value 'myvalue' for unique attribute 'mycolumn' from entity 'mytable'."); @@ -133,7 +133,7 @@ public void translateUniqueKeyViolationBadMessage() when(serverErrorMessage.getTable()).thenReturn("mytable"); when(serverErrorMessage.getDetail()).thenReturn("xxxyyyyzzz"); //noinspection ThrowableResultOfMethodCallIgnored - postgreSqlExceptionTranslator.translateUniqueKeyViolation(new PSQLException(serverErrorMessage)); + PostgreSqlExceptionTranslator.translateUniqueKeyViolation(new PSQLException(serverErrorMessage)); } @Test @@ -144,7 +144,7 @@ public void translateInvalidIntegerExceptionInteger() ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for integer: \"str1\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [INT or LONG]."); } @@ -157,7 +157,7 @@ public void translateInvalidIntegerExceptionBoolean() ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type boolean: \"str1\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [BOOL]."); } @@ -170,7 +170,7 @@ public void translateInvalidIntegerExceptionDouble() ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type double precision: \"str1\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DECIMAL]."); } @@ -183,7 +183,7 @@ public void translateInvalidIntegerExceptionDate() ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type date: \"str1\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DATE]."); } @@ -196,7 +196,7 @@ public void translateInvalidIntegerExceptionDateTime() ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); when(serverErrorMessage.getMessage()).thenReturn("invalid input syntax for type timestamp: \"str1\""); //noinspection ThrowableResultOfMethodCallIgnored - MolgenisValidationException e = postgreSqlExceptionTranslator + MolgenisValidationException e = PostgreSqlExceptionTranslator .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DATE_TIME]."); } From 0a638ad9dce0e4ffe512750404874ef3ff3bf04c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 11:46:52 +0200 Subject: [PATCH 21/49] Throw MolgenisDataException with clear error message in case of an unknown backend --- .../EntityMetaDataRepositoryDecorator.java | 7 +++- ...EntityMetaDataRepositoryDecoratorTest.java | 38 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java index 31b391e3297..11c30967009 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java @@ -352,7 +352,12 @@ private void addEntityMetaData(EntityMetaData entityMetaData) decoratedRepo.add(entityMetaData); if (!entityMetaData.isAbstract() && !dataService.getMeta().isMetaEntityMetaData(entityMetaData)) { - dataService.getMeta().getBackend(entityMetaData.getBackend()).createRepository(entityMetaData); + RepositoryCollection repoCollection = dataService.getMeta().getBackend(entityMetaData.getBackend()); + if (repoCollection == null) + { + throw new MolgenisDataException(format("Unknown backend [%s]", entityMetaData.getBackend())); + } + repoCollection.createRepository(entityMetaData); } } diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java index 3ba3c51bab5..ba5aac29ebe 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java @@ -19,8 +19,7 @@ import static com.google.common.collect.Lists.newArrayList; import static java.util.Arrays.asList; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; +import static java.util.Collections.*; import static java.util.stream.Collectors.toList; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Mockito.*; @@ -127,6 +126,41 @@ public void countUser() throws Exception assertEquals(repo.count(), 1L); } + @Test + public void addWithKnownBackend() + { + SecurityContextHolder.getContext() + .setAuthentication(new TestingAuthenticationToken("anonymous", null, "ROLE_SU")); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.getSimpleName()).thenReturn("entity"); + when(entityMeta.getAttributes()).thenReturn(emptyList()); + String backendName = "knownBackend"; + when(entityMeta.getBackend()).thenReturn(backendName); + MetaDataService metaDataService = mock(MetaDataService.class); + RepositoryCollection repoCollection = mock(RepositoryCollection.class); + when(metaDataService.getBackend(backendName)).thenReturn(repoCollection); + when(dataService.getMeta()).thenReturn(metaDataService); + repo.add(entityMeta); + verify(decoratedRepo).add(entityMeta); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Unknown backend \\[unknownBackend\\]") + public void addWithUnknownBackend() + { + SecurityContextHolder.getContext() + .setAuthentication(new TestingAuthenticationToken("anonymous", null, "ROLE_SU")); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.getSimpleName()).thenReturn("entity"); + when(entityMeta.getAttributes()).thenReturn(emptyList()); + String backendName = "unknownBackend"; + when(entityMeta.getBackend()).thenReturn(backendName); + MetaDataService metaDataService = mock(MetaDataService.class); + when(metaDataService.getBackend(backendName)).thenReturn(null); + when(dataService.getMeta()).thenReturn(metaDataService); + repo.add(entityMeta); + verify(decoratedRepo).add(entityMeta); + } + @Test public void query() throws Exception { From 86e2767163dbbe58fcad369a7bcb0b513e8f01f8 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 14:34:46 +0200 Subject: [PATCH 22/49] Fix #5198 PostgreSQL junction tables index on id attribute --- .../postgresql/PostgreSqlQueryGenerator.java | 17 ++++---- .../data/postgresql/PostgreSqlQueryUtils.java | 43 ++++++++++++------- .../PostgreSqlRepositoryCollection.java | 12 ++++++ .../PostgreSqlQueryGeneratorTest.java | 15 ++++++- .../postgresql/PostgreSqlQueryUtilsTest.java | 22 ++++++++++ 5 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryUtilsTest.java diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java index c48b34c729f..aa64ce58eb0 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryGenerator.java @@ -171,7 +171,7 @@ static String getSqlCreateTable(EntityMetaData entityMeta) static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { AttributeMetaData idAttr = entityMeta.getIdAttribute(); - StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS ") + StringBuilder sql = new StringBuilder("CREATE TABLE ") .append(getJunctionTableName(entityMeta, attr)).append(" (") .append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(" INT,").append(getColumnName(idAttr)) .append(' ').append(getPostgreSqlType(idAttr)).append(" NOT NULL, ").append(getColumnName(attr)) @@ -199,7 +199,7 @@ static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMeta } } - sql.append(", UNIQUE (").append(getColumnName(attr)).append(',').append(getColumnName(idAttr)).append(')'); + sql.append(", UNIQUE (").append(getColumnName(idAttr)).append(',').append(getColumnName(attr)).append(')'); sql.append(", UNIQUE (").append(getColumnName(JUNCTION_TABLE_ORDER_ATTR_NAME)).append(',') .append(getColumnName(idAttr)).append(')'); @@ -210,10 +210,11 @@ static String getSqlCreateJunctionTable(EntityMetaData entityMeta, AttributeMeta static String getSqlCreateJunctionTableIndex(EntityMetaData entityMeta, AttributeMetaData attr) { + AttributeMetaData idxAttr = entityMeta.getIdAttribute(); String junctionTableName = getJunctionTableName(entityMeta, attr); - return "CREATE INDEX " + junctionTableName.substring(0, junctionTableName.length() - 1) + '_' + entityMeta - .getIdAttribute().getName() + "_index\" ON " + junctionTableName + " (" + getColumnName( - entityMeta.getIdAttribute()) + ')'; + String junctionTableIndexName = getJunctionTableIndexName(entityMeta, attr, idxAttr); + String idxColumnName = getColumnName(idxAttr); + return "CREATE INDEX " + junctionTableIndexName + " ON " + junctionTableName + " (" + idxColumnName + ')'; } static String getSqlDropJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) @@ -806,12 +807,12 @@ private static String getColumnName(AttributeMetaData attr) private static String getColumnName(String attrName) { - return "\"" + attrName + '"'; + return '"' + attrName + '"'; } private static String getFilterColumnName(AttributeMetaData attr, int filterIndex) { - return "\"" + attr.getName() + "_filter" + filterIndex + '"'; + return '"' + attr.getName() + "_filter" + filterIndex + '"'; } private static String getPrimaryKeyName(EntityMetaData entityMeta, AttributeMetaData attr) @@ -836,7 +837,7 @@ private static String getCheckConstraintName(EntityMetaData entityMeta, Attribut private static String getConstraintName(EntityMetaData entityMeta, AttributeMetaData attr, String constraintPostfix) { - return "\"" + entityMeta.getName() + '_' + attr.getName() + '_' + constraintPostfix + '"'; + return '"' + entityMeta.getName() + '_' + attr.getName() + '_' + constraintPostfix + '"'; } private static List getMrefQueryAttrs(EntityMetaData entityMeta, Query q) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryUtils.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryUtils.java index 02784822a47..05d5fd95885 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryUtils.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlQueryUtils.java @@ -28,7 +28,7 @@ private PostgreSqlQueryUtils() * @param entityMeta entity meta data * @return table name for this entity */ - public static String getTableName(EntityMetaData entityMeta) + static String getTableName(EntityMetaData entityMeta) { return getTableName(entityMeta, true); } @@ -39,32 +39,45 @@ public static String getTableName(EntityMetaData entityMeta) * @param entityMeta entity meta data * @return PostgreSQL table name */ - public static String getTableName(EntityMetaData entityMeta, boolean quoteSystemIdentifiers) + static String getTableName(EntityMetaData entityMeta, boolean quoteSystemIdentifiers) { - StringBuilder strBuilder = new StringBuilder(); + StringBuilder strBuilder = new StringBuilder(32); if (quoteSystemIdentifiers) { - strBuilder.append("\""); + strBuilder.append('"'); } strBuilder.append(entityMeta.getName()); if (quoteSystemIdentifiers) { - strBuilder.append("\""); + strBuilder.append('"'); } return strBuilder.toString(); } /** - * Returns the function table name for the given attribute of the given entity + * Returns the junction table name for the given attribute of the given entity * - * @param emd entity meta data that owns the attribute - * @param attr attribute + * @param entityMeta entity meta data that owns the attribute + * @param attr attribute * @return PostgreSQL junction table name */ - public static String getJunctionTableName(EntityMetaData emd, AttributeMetaData attr) + static String getJunctionTableName(EntityMetaData entityMeta, AttributeMetaData attr) { - return new StringBuilder().append("\"").append(emd.getName()).append('_').append(attr.getName()).append("\"") - .toString(); + return '"' + entityMeta.getName() + '_' + attr.getName() + '"'; + } + + /** + * Returns the junction table index name for the given indexed attribute in a junction table + * + * @param entityMeta entity meta data + * @param attr attribute + * @param idxAttr indexed attribute + * @return PostgreSQL junction table index name + */ + static String getJunctionTableIndexName(EntityMetaData entityMeta, AttributeMetaData attr, + AttributeMetaData idxAttr) + { + return '"' + entityMeta.getName() + '_' + attr.getName() + '_' + idxAttr.getName() + "_idx\""; } /** @@ -72,7 +85,7 @@ public static String getJunctionTableName(EntityMetaData emd, AttributeMetaData * * @return stream of persisted attributes */ - public static Stream getPersistedAttributes(EntityMetaData entityMeta) + static Stream getPersistedAttributes(EntityMetaData entityMeta) { return StreamSupport.stream(entityMeta.getAtomicAttributes().spliterator(), false) .filter(atomicAttr -> atomicAttr.getExpression() == null); @@ -84,7 +97,7 @@ public static Stream getPersistedAttributes(EntityMetaData en * * @return stream of persisted MREF attributes */ - public static Stream getPersistedAttributesMref(EntityMetaData entityMeta) + static Stream getPersistedAttributesMref(EntityMetaData entityMeta) { return getPersistedAttributes(entityMeta).filter(EntityMetaDataUtils::isMultipleReferenceType); } @@ -95,7 +108,7 @@ public static Stream getPersistedAttributesMref(EntityMetaDat * * @return stream of persisted non-MREF attributes */ - public static Stream getPersistedAttributesNonMref(EntityMetaData entityMeta) + static Stream getPersistedAttributesNonMref(EntityMetaData entityMeta) { return getPersistedAttributes(entityMeta).filter(attr -> !isMultipleReferenceType(attr)); } @@ -106,7 +119,7 @@ public static Stream getPersistedAttributesNonMref(EntityMeta * @param entityMeta entity meta data * @return true is the entity is persisted in PostgreSQL */ - public static boolean isPersistedInPostgreSql(EntityMetaData entityMeta) + static boolean isPersistedInPostgreSql(EntityMetaData entityMeta) { String backend = entityMeta.getBackend(); if (backend == null) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 2ffffe3868c..2cf1ab43ff0 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -687,5 +687,17 @@ private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData at } } jdbcTemplate.execute(createJunctionTableSql); + + String createJunctionTableIndexSql = getSqlCreateJunctionTableIndex(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Creating junction table index for entity [{}] attribute [{}]", entityMeta.getName(), + attr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", createJunctionTableIndexSql); + } + } + jdbcTemplate.execute(createJunctionTableIndexSql); } } \ No newline at end of file diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java index 1df08ca13a8..cd701747fbd 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryGeneratorTest.java @@ -255,7 +255,7 @@ public void getSqlCreateJunctionTable() when(attr.getDataType()).thenReturn(MREF); when(attr.getRefEntity()).thenReturn(refEntityMeta); - String expectedSql = "CREATE TABLE IF NOT EXISTS \"entity_attr\" (\"order\" INT,\"idAttr\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"idAttr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE, FOREIGN KEY (\"attr\") REFERENCES \"refEntity\"(\"refIdAttr\") ON DELETE CASCADE, UNIQUE (\"attr\",\"idAttr\"), UNIQUE (\"order\",\"idAttr\"))"; + String expectedSql = "CREATE TABLE \"entity_attr\" (\"order\" INT,\"idAttr\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"idAttr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE, FOREIGN KEY (\"attr\") REFERENCES \"refEntity\"(\"refIdAttr\") ON DELETE CASCADE, UNIQUE (\"idAttr\",\"attr\"), UNIQUE (\"order\",\"idAttr\"))"; assertEquals(PostgreSqlQueryGenerator.getSqlCreateJunctionTable(entityMeta, attr), expectedSql); } @@ -270,7 +270,7 @@ public void getSqlCreateJunctionTableSelfReferencing() when(attr.getDataType()).thenReturn(MREF); when(attr.getRefEntity()).thenReturn(entityMeta); - String expectedSql = "CREATE TABLE IF NOT EXISTS \"entity_attr\" (\"order\" INT,\"idAttr\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"idAttr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (\"attr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (\"attr\",\"idAttr\"), UNIQUE (\"order\",\"idAttr\"))"; + String expectedSql = "CREATE TABLE \"entity_attr\" (\"order\" INT,\"idAttr\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"idAttr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (\"attr\") REFERENCES \"entity\"(\"idAttr\") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (\"idAttr\",\"attr\"), UNIQUE (\"order\",\"idAttr\"))"; assertEquals(PostgreSqlQueryGenerator.getSqlCreateJunctionTable(entityMeta, attr), expectedSql); } @@ -426,4 +426,15 @@ public void getSqlDropColumn() assertEquals(PostgreSqlQueryGenerator.getSqlDropColumn(entityMeta, attr), "ALTER TABLE \"entity\" DROP COLUMN \"attr\""); } + + @Test + public void getSqlCreateJunctionTableIndex() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + AttributeMetaData idxAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("idxAttr").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idxAttr); + assertEquals(PostgreSqlQueryGenerator.getSqlCreateJunctionTableIndex(entityMeta, attr), + "CREATE INDEX \"entity_attr_idxAttr_idx\" ON \"entity_attr\" (\"idxAttr\")"); + } } \ No newline at end of file diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryUtilsTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryUtilsTest.java new file mode 100644 index 00000000000..2969131012a --- /dev/null +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlQueryUtilsTest.java @@ -0,0 +1,22 @@ +package org.molgenis.data.postgresql; + +import org.molgenis.data.meta.model.AttributeMetaData; +import org.molgenis.data.meta.model.EntityMetaData; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +public class PostgreSqlQueryUtilsTest +{ + @Test + public void getJunctionTableIndexName() throws Exception + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn("attr").getMock(); + AttributeMetaData idxAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("idxAttr").getMock(); + assertEquals(PostgreSqlQueryUtils.getJunctionTableIndexName(entityMeta, attr, idxAttr), + "\"entity_attr_idxAttr_idx\""); + } +} \ No newline at end of file From aa54d1ac79f60887dc5b93462d2524151ec23d60 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 14:48:50 +0200 Subject: [PATCH 23/49] Set simple name to fully qualified entity name in case simple name is null --- .../data/meta/model/EntityMetaData.java | 18 +++++++++++++ .../data/meta/model/EntityMetaDataTest.java | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java index ddc9339aa6e..b029d9fc18c 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java @@ -152,9 +152,20 @@ public String getName() return getString(FULL_NAME); } + /** + * Sets the fully qualified entity name. + * In case this entity simple name is null, assigns the fully qualified entity name to the simple name. + * + * @param fullName fully qualified entity name. + * @return this entity meta data for chaining + */ public EntityMetaData setName(String fullName) { set(FULL_NAME, fullName); + if (getSimpleName() == null) + { + setSimpleName(fullName); + } return this; } @@ -168,6 +179,13 @@ public String getSimpleName() return getString(SIMPLE_NAME); } + /** + * Sets the entity name. + * In case this entity label is null, assigns the entity name to the label. + * + * @param simpleName entity name. + * @return this entity meta data for chaining + */ public EntityMetaData setSimpleName(String simpleName) { set(SIMPLE_NAME, simpleName); diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java index 728e060639c..1482286e5fa 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/model/EntityMetaDataTest.java @@ -68,6 +68,32 @@ public void beforeMethod() nestedEntityMetaData = new EntityMetaData(nestedEntity); } + @Test + public void setNameNoSimpleNameNoLabel() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String name = "name"; + entityMeta.setName(name); + assertEquals(entityMeta.getName(), name); + assertEquals(entityMeta.getSimpleName(), name); + assertEquals(entityMeta.getLabel(), name); + } + + @Test + public void setNameExistingSimpleNameExistingLabel() + { + EntityMetaData entityMeta = new EntityMetaData(createEntityMetaMeta()); + String label = "label"; + String simpleName = "simpleName"; + String name = "name"; + entityMeta.setLabel(label); + entityMeta.setSimpleName(simpleName); + entityMeta.setName(name); + assertEquals(entityMeta.getName(), name); + assertEquals(entityMeta.getSimpleName(), simpleName); + assertEquals(entityMeta.getLabel(), label); + } + @Test public void setSimpleNameNoNameNoLabel() { From aaca2ce884defb31fc734b494b46f52fadb0cd57 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:03:17 +0200 Subject: [PATCH 24/49] Set simple name to fully qualified entity name in case simple name is null (small fix) --- .../java/org/molgenis/data/meta/model/EntityMetaData.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java index b029d9fc18c..249d24aeceb 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaData.java @@ -164,7 +164,11 @@ public EntityMetaData setName(String fullName) set(FULL_NAME, fullName); if (getSimpleName() == null) { - setSimpleName(fullName); + set(SIMPLE_NAME, fullName); + } + if (getLabel() == null) + { + set(LABEL, fullName); } return this; } From a6cce9cdac8d2645ed9f35a2f42ebcec48f2826a Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:05:59 +0200 Subject: [PATCH 25/49] Fix FindBugs warning: field AbstractMolgenisSpringTest.applicationContext masks field in superclass org.springframework.test.context.testng.AbstractTestNGSpringContextTests --- .../org/molgenis/test/data/AbstractMolgenisSpringTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/molgenis-test/src/main/java/org/molgenis/test/data/AbstractMolgenisSpringTest.java b/molgenis-test/src/main/java/org/molgenis/test/data/AbstractMolgenisSpringTest.java index 12d8bcfffe5..46cf5e65db8 100644 --- a/molgenis-test/src/main/java/org/molgenis/test/data/AbstractMolgenisSpringTest.java +++ b/molgenis-test/src/main/java/org/molgenis/test/data/AbstractMolgenisSpringTest.java @@ -4,8 +4,6 @@ import org.molgenis.data.meta.model.AttributeMetaDataMetaData; import org.molgenis.data.meta.model.EntityMetaDataMetaData; import org.molgenis.util.GenericDependencyResolver; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -18,9 +16,6 @@ @ContextConfiguration(classes = { AbstractMolgenisSpringTest.Config.class }) public abstract class AbstractMolgenisSpringTest extends AbstractTestNGSpringContextTests { - @Autowired - protected ApplicationContext applicationContext; - @BeforeClass public void bootstrap() { From d1ac6c9b9665140b3fd3ac0b58b1cf0bbe3b1a35 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:06:45 +0200 Subject: [PATCH 26/49] Fix FindBugs warning: format string "%s" needs argument 1 but only 0 are provided in org.molgenis.security.account.AccountServiceImpl.changePassword(String, String) --- .../java/org/molgenis/security/account/AccountServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-security/src/main/java/org/molgenis/security/account/AccountServiceImpl.java b/molgenis-security/src/main/java/org/molgenis/security/account/AccountServiceImpl.java index ceb734089dd..cc6d448b256 100644 --- a/molgenis-security/src/main/java/org/molgenis/security/account/AccountServiceImpl.java +++ b/molgenis-security/src/main/java/org/molgenis/security/account/AccountServiceImpl.java @@ -169,7 +169,7 @@ public void changePassword(String username, String newPassword) MolgenisUser user = dataService.query(MOLGENIS_USER, MolgenisUser.class).eq(USERNAME, username).findOne(); if (user == null) { - throw new MolgenisUserException(format("Unknown user [%s]")); + throw new MolgenisUserException(format("Unknown user [%s]", username)); } user.setPassword(newPassword); From 77dcbacd805f12e14d5529cb9141342b207bd60d Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:10:05 +0200 Subject: [PATCH 27/49] Fix FindBugs warning: org.molgenis.ontology.sorta.bean.OntologyTermHitEntity defines equals and uses Object.hashCode() --- .../ontology/sorta/bean/OntologyTermHitEntity.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/bean/OntologyTermHitEntity.java b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/bean/OntologyTermHitEntity.java index 3f141c8cb8a..e5f452c3111 100644 --- a/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/bean/OntologyTermHitEntity.java +++ b/molgenis-ontology/src/main/java/org/molgenis/ontology/sorta/bean/OntologyTermHitEntity.java @@ -28,4 +28,11 @@ public boolean equals(Object obj) DynamicEntity other = (DynamicEntity) obj; return getIdValue().equals(other.getIdValue()); } + + @Override + public int hashCode() + { + Object idValue = getIdValue(); + return idValue != null ? getIdValue().hashCode() : 0; + } } From 8bbd525bd49e6f7af1c63c81af7c92f511ffd8f7 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:11:10 +0200 Subject: [PATCH 28/49] Fix FindBugs warning: format string "%s" needs argument 4 but only 3 are provided in org.molgenis.data.rest.service.RestService.convertDate(AttributeMetaData, Object) --- .../main/java/org/molgenis/data/rest/service/RestService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java index 17d6d8dbb48..580cb70560b 100644 --- a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java +++ b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java @@ -327,7 +327,8 @@ else if (paramValue instanceof String) { throw new MolgenisDataException( format("Attribute [%s] value [%s] does not match date format [%s] or [%s]", attr.getName(), - paramStrValue, MolgenisDateFormat.DATEFORMAT_DATE)); + paramStrValue, MolgenisDateFormat.DATEFORMAT_DATE, + MolgenisDateFormat.DATEFORMAT_DATETIME)); } } else From d7b59d8e4212f95beccf2e40ce8d4dcf5200018a Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:13:00 +0200 Subject: [PATCH 29/49] Fix FindBugs warning: Nullcheck of request at line xxx of value previously dereferenced in org.molgenis.data.rest.RestController.retrieveEntityAttributePost(String, String, String, EntityCollectionRequest) --- .../java/org/molgenis/data/rest/RestController.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/RestController.java b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/RestController.java index 141f0187bff..5a5a3ce4444 100644 --- a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/RestController.java +++ b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/RestController.java @@ -326,8 +326,8 @@ public Object retrieveEntityAttributePost(@PathVariable("entityName") String ent @Valid @RequestBody EntityCollectionRequest request) { validateAndConvertQueryValues(request.getQ(), dataService.getEntityMetaData(entityName)); - Set attributesSet = toAttributeSet(request != null ? request.getAttributes() : null); - Map> attributeExpandSet = toExpandMap(request != null ? request.getExpand() : null); + Set attributesSet = toAttributeSet(request.getAttributes()); + Map> attributeExpandSet = toExpandMap(request.getExpand()); return retrieveEntityAttributeInternal(entityName, untypedId, refAttributeName, request, attributesSet, attributeExpandSet); @@ -375,10 +375,8 @@ public EntityCollectionResponse retrieveEntityCollectionPost(@PathVariable("enti @Valid @RequestBody EntityCollectionRequest request) { validateAndConvertQueryValues(request.getQ(), dataService.getEntityMetaData(entityName)); - Set attributesSet = toAttributeSet(request != null ? request.getAttributes() : null); - Map> attributeExpandSet = toExpandMap(request != null ? request.getExpand() : null); - - request = request != null ? request : new EntityCollectionRequest(); + Set attributesSet = toAttributeSet(request.getAttributes()); + Map> attributeExpandSet = toExpandMap(request.getExpand()); return retrieveEntityCollectionInternal(entityName, request, attributesSet, attributeExpandSet); } From ace11f3ad66379705869060fb973d764930ba120 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:13:49 +0200 Subject: [PATCH 30/49] Fix FindBugs warning: org.molgenis.data.importer.EmxMetaDataParser.EMX_NAME_TO_REPO_NAME_MAP isn't final but should be --- .../main/java/org/molgenis/data/importer/EmxMetaDataParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java index db7a7ef663d..ba99d810990 100644 --- a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java +++ b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java @@ -94,7 +94,7 @@ public class EmxMetaDataParser implements MetaDataParser public static final String EMX_PACKAGE_TAGS = "tags"; public static final String EMX_PACKAGE_LABEL = "label"; - public static Map EMX_NAME_TO_REPO_NAME_MAP = newHashMap(); + public static final Map EMX_NAME_TO_REPO_NAME_MAP = newHashMap(); static { From b744ebd0fc455acb2a6b176e9fbaf7bc36110c38 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:17:09 +0200 Subject: [PATCH 31/49] Ignore FindBugs warning: org.molgenis.data.cache.utils.CombinedEntityCache.getIfPresent(EntityMetaData, Object) has Optional return type and returns explicit null --- .../java/org/molgenis/data/cache/utils/CombinedEntityCache.java | 1 + 1 file changed, 1 insertion(+) diff --git a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/utils/CombinedEntityCache.java b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/utils/CombinedEntityCache.java index 606ac81ab44..e4c750056e7 100644 --- a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/utils/CombinedEntityCache.java +++ b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/utils/CombinedEntityCache.java @@ -60,6 +60,7 @@ public void evictAll(String entityName) * @return Optional {@link Entity} with the result from the cache, * or null if no record of the entity is present in the cache */ + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_OPTIONAL_RETURN_NULL", justification = "Intentional behavior") public Optional getIfPresent(EntityMetaData entityMetaData, Object id) { Optional> optionalDehydratedEntity = cache From 02c40de983849b40c3c63bed8a98a49e1c644b12 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:18:05 +0200 Subject: [PATCH 32/49] Ignore FindBugs warning: org.molgenis.data.cache.l1.L1Cache.get(String, Object, EntityMetaData) has Optional return type and returns explicit null --- .../src/main/java/org/molgenis/data/cache/l1/L1Cache.java | 1 + 1 file changed, 1 insertion(+) diff --git a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1Cache.java b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1Cache.java index 4d8247f569e..0b7282146ca 100644 --- a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1Cache.java +++ b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1Cache.java @@ -92,6 +92,7 @@ public void evictAll(String entityName) * @return the retrieved {@link Entity} or Optional.empty() if deletion of this entity is stored in the cache or * null if no information available about this entity in the cache */ + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_OPTIONAL_RETURN_NULL", justification = "Intentional behavior") public Optional get(String entityName, Object id, EntityMetaData entityMetaData) { CombinedEntityCache cache = caches.get(); From 0475c9bfc7cd190e48851b14ea547199344dee39 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:19:57 +0200 Subject: [PATCH 33/49] Fix FindBugs warning: boxing/unboxing to parse a primitive new org.molgenis.data.annotation.core.entity.impl.gavin.GavinEntry(String) --- .../core/entity/impl/gavin/GavinEntry.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/entity/impl/gavin/GavinEntry.java b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/entity/impl/gavin/GavinEntry.java index 73c1a8d3108..7a894330959 100644 --- a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/entity/impl/gavin/GavinEntry.java +++ b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/entity/impl/gavin/GavinEntry.java @@ -49,12 +49,12 @@ public GavinEntry(String lineFromFile) throws Exception this.gene = split[0]; this.category = Category.valueOf(split[1]); this.chromosome = split[2]; - this.start = Integer.valueOf(split[3]); - this.end = Integer.valueOf(split[4]); - this.NrOfPopulationVariants = Integer.valueOf(split[5]); - this.NrOfPathogenicVariants = Integer.valueOf(split[6]); - this.NrOfOverlappingVariants = Integer.valueOf(split[7]); - this.NrOfFilteredPopVariants = Integer.valueOf(split[8]); + this.start = Integer.parseInt(split[3]); + this.end = Integer.parseInt(split[4]); + this.NrOfPopulationVariants = Integer.parseInt(split[5]); + this.NrOfPathogenicVariants = Integer.parseInt(split[6]); + this.NrOfOverlappingVariants = Integer.parseInt(split[7]); + this.NrOfFilteredPopVariants = Integer.parseInt(split[8]); this.PathoMAFThreshold = split[9].isEmpty() ? null : Double.parseDouble(split[9]); this.PopImpactHighPerc = split[10].isEmpty() ? null : Double.parseDouble(split[10]); From c552823073fc78782a7efc21172472f6b262305f Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:21:57 +0200 Subject: [PATCH 34/49] Fix FindBugs warning: org.molgenis.data.annotation.core.effects.EffectsMetaData.xxx isn't final but should be --- .../core/effects/EffectsMetaData.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/effects/EffectsMetaData.java b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/effects/EffectsMetaData.java index e3fb301010e..c4878eb33ea 100644 --- a/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/effects/EffectsMetaData.java +++ b/molgenis-data-annotators/src/main/java/org/molgenis/data/annotation/core/effects/EffectsMetaData.java @@ -21,22 +21,22 @@ public class EffectsMetaData implements AnnotatorEntityMetaData public static String ID = "id"; public static String VARIANT = VcfWriterUtils.VARIANT; - public static String ALT = "Alt_Allele"; - public static String ANNOTATION = "Annotation"; - public static String PUTATIVE_IMPACT = "Putative_impact"; - public static String GENE_NAME = "Gene_Name"; - public static String GENE_ID = "Gene_ID"; - public static String FEATURE_TYPE = "Feature_type"; - public static String FEATURE_ID = "Feature_ID"; - public static String TRANSCRIPT_BIOTYPE = "Transcript_biotype"; - public static String RANK_TOTAL = "Rank_total"; - public static String HGVS_C = "HGVS_c"; - public static String HGVS_P = "HGVS_p"; - public static String C_DNA_POSITION = "cDNA_position"; - public static String CDS_POSITION = "CDS_position"; - public static String PROTEIN_POSITION = "Protein_position"; - public static String DISTANCE_TO_FEATURE = "Distance_to_feature"; - public static String ERRORS = "Errors"; + public static final String ALT = "Alt_Allele"; + public static final String ANNOTATION = "Annotation"; + public static final String PUTATIVE_IMPACT = "Putative_impact"; + public static final String GENE_NAME = "Gene_Name"; + public static final String GENE_ID = "Gene_ID"; + public static final String FEATURE_TYPE = "Feature_type"; + public static final String FEATURE_ID = "Feature_ID"; + public static final String TRANSCRIPT_BIOTYPE = "Transcript_biotype"; + public static final String RANK_TOTAL = "Rank_total"; + public static final String HGVS_C = "HGVS_c"; + public static final String HGVS_P = "HGVS_p"; + public static final String C_DNA_POSITION = "cDNA_position"; + public static final String CDS_POSITION = "CDS_position"; + public static final String PROTEIN_POSITION = "Protein_position"; + public static final String DISTANCE_TO_FEATURE = "Distance_to_feature"; + public static final String ERRORS = "Errors"; public LinkedList getOrderedAttributes() { From 93744129342a68b8865f75c18fc124ac72155e13 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 15:55:37 +0200 Subject: [PATCH 35/49] Fix #1807 Updating ID attribute results in exception Refactor: remove unused deleteEntityTags method from entity meta data decorator (tag can belong to multiple entities) --- .../data/meta/EntityMetaDataRepositoryDecorator.java | 8 -------- .../molgenis/data/meta/model/EntityMetaDataMetaData.java | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java index 31b391e3297..a07e4de5c61 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java @@ -5,7 +5,6 @@ import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; -import org.molgenis.data.meta.model.Tag; import org.molgenis.data.meta.system.SystemEntityMetaDataRegistry; import org.molgenis.data.support.QueryImpl; import org.molgenis.security.core.MolgenisPermissionService; @@ -35,7 +34,6 @@ import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.ATTRIBUTE_META_DATA; import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.NAME; import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; -import static org.molgenis.data.meta.model.TagMetaData.TAG; import static org.molgenis.security.core.Permission.COUNT; import static org.molgenis.security.core.Permission.READ; import static org.molgenis.security.core.utils.SecurityUtils.currentUserIsSu; @@ -502,12 +500,6 @@ private void deleteEntityAttributes(EntityMetaData entityMetaData) dataService.delete(ATTRIBUTE_META_DATA, allAttrs); } - private void deleteEntityTags(EntityMetaData entityMetaData) - { - Iterable tags = entityMetaData.getTags(); - dataService.delete(TAG, StreamSupport.stream(tags.spliterator(), false)); - } - private void deleteEntityRepository(EntityMetaData entityMetaData) { String backend = entityMetaData.getBackend(); diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java index 168f966968f..43ecbedae26 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java @@ -52,7 +52,8 @@ public void init() addAttribute(LABEL, ROLE_LOOKUP).setLabel("Label"); addAttribute(DESCRIPTION).setDataType(TEXT).setLabel("Description"); addAttribute(ATTRIBUTES).setDataType(MREF).setRefEntity(attrMetaMeta).setNillable(false).setLabel("Attributes"); - addAttribute(ID_ATTRIBUTE).setDataType(XREF).setRefEntity(attrMetaMeta).setLabel("ID attribute"); + addAttribute(ID_ATTRIBUTE).setDataType(XREF).setRefEntity(attrMetaMeta).setReadOnly(true) + .setLabel("ID attribute"); addAttribute(LABEL_ATTRIBUTE).setDataType(XREF).setRefEntity(attrMetaMeta).setLabel("Label attribute"); addAttribute(LOOKUP_ATTRIBUTES).setDataType(MREF).setRefEntity(attrMetaMeta).setLabel("Lookup attributes"); addAttribute(ABSTRACT).setDataType(BOOL).setNillable(false).setReadOnly(true).setLabel("Abstract") From b909b87bcbe2a3d19b8fa772bb5e6ac1e0da37f1 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 17 Aug 2016 16:39:35 +0200 Subject: [PATCH 36/49] Fix #4897 Remove LoggingEvent --- .../logback/LoggingEventMetaData.java | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java diff --git a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java b/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java deleted file mode 100644 index 4941df42bb8..00000000000 --- a/molgenis-data-elasticsearch/src/main/java/org/molgenis/data/elasticsearch/logback/LoggingEventMetaData.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.molgenis.data.elasticsearch.logback; - -import org.molgenis.data.meta.SystemEntityMetaData; -import org.springframework.stereotype.Component; - -import static org.molgenis.MolgenisFieldTypes.AttributeType.DATE_TIME; -import static org.molgenis.MolgenisFieldTypes.AttributeType.TEXT; -import static org.molgenis.data.meta.model.EntityMetaData.AttributeRole.ROLE_ID; -import static org.molgenis.data.meta.model.Package.PACKAGE_SEPARATOR; -import static org.molgenis.data.system.model.RootSystemPackage.PACKAGE_SYSTEM; - -@Component -public class LoggingEventMetaData extends SystemEntityMetaData -{ - private static final String SIMPLE_NAME = "LoggingEvent"; - public static final String LOGGING_EVENT = PACKAGE_SYSTEM + PACKAGE_SEPARATOR + SIMPLE_NAME; - - public static final String IDENTIFIER = "identifier"; - public static final String THREAD = "thread"; - public static final String LEVEL = "level"; - public static final String LOGGER = "logger"; - public static final String MESSAGE = "message"; - public static final String TIMESTAMP = "timestamp"; - public static final String STACKTRACE = "stacktrace"; - - LoggingEventMetaData() - { - super(SIMPLE_NAME, PACKAGE_SYSTEM); - } - - @Override - public void init() - { - addAttribute(IDENTIFIER, ROLE_ID).setVisible(false); - addAttribute(TIMESTAMP).setDataType(DATE_TIME); - addAttribute(THREAD); - addAttribute(LEVEL); - addAttribute(LOGGER); - addAttribute(MESSAGE).setDataType(TEXT); - addAttribute(STACKTRACE).setDataType(TEXT); - } -} From d2aaf8b612f0cfbc68649ef64d9506676fb37bad Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 18 Aug 2016 11:46:42 +0200 Subject: [PATCH 37/49] Fix #5206 Entity meta data validation missing checks Add generic for entity type to AbstractRepositoryDecorator Move entity meta data validation to seperate bean, create separate decorator --- .../cache/l1/L1CacheRepositoryDecorator.java | 2 +- .../cache/l2/L2CacheRepositoryDecorator.java | 2 +- .../data/importer/EmxMetaDataParser.java | 1 + .../MolgenisRepositoryDecoratorFactory.java | 9 +- .../data/validation/ConstraintViolation.java | 5 + .../MolgenisValidationException.java | 6 + ...MetaDataRepositoryValidationDecorator.java | 65 ++++ .../meta/EntityMetaDataValidator.java | 212 ++++++++++++ ...DataRepositoryValidationDecoratorTest.java | 130 ++++++++ .../meta/EntityMetaDataValidatorTest.java | 306 ++++++++++++++++++ .../data/AbstractRepositoryDecorator.java | 36 +-- .../EntityMetaDataRepositoryDecorator.java | 33 +- .../data/meta/MetaValidationUtils.java | 20 +- .../ReindexActionRepositoryDecorator.java | 2 +- 14 files changed, 767 insertions(+), 62 deletions(-) create mode 100644 molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecorator.java create mode 100644 molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java create mode 100644 molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecoratorTest.java create mode 100644 molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataValidatorTest.java diff --git a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1CacheRepositoryDecorator.java b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1CacheRepositoryDecorator.java index 8503fb343c6..b92e4afe885 100644 --- a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1CacheRepositoryDecorator.java +++ b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l1/L1CacheRepositoryDecorator.java @@ -28,7 +28,7 @@ * Delegates to the underlying repository when an action is not supported by the cache or when the cache doesn't contain * the needed entity. */ -public class L1CacheRepositoryDecorator extends AbstractRepositoryDecorator +public class L1CacheRepositoryDecorator extends AbstractRepositoryDecorator { private static final int ID_BATCH_SIZE = 1000; diff --git a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/L2CacheRepositoryDecorator.java b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/L2CacheRepositoryDecorator.java index 8869d1414d1..8a03982e57d 100644 --- a/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/L2CacheRepositoryDecorator.java +++ b/molgenis-data-cache/src/main/java/org/molgenis/data/cache/l2/L2CacheRepositoryDecorator.java @@ -32,7 +32,7 @@ * Delegates to the underlying repository when an action is not supported by the cache or when the cache doesn't contain * the needed entity. */ -public class L2CacheRepositoryDecorator extends AbstractRepositoryDecorator +public class L2CacheRepositoryDecorator extends AbstractRepositoryDecorator { private static final int ID_BATCH_SIZE = 1000; diff --git a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java index db7a7ef663d..31fed75a390 100644 --- a/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java +++ b/molgenis-data-import/src/main/java/org/molgenis/data/importer/EmxMetaDataParser.java @@ -231,6 +231,7 @@ private ImmutableMap getEntityMetaDataMap(DataService da private EntitiesValidationReport buildValidationReport(RepositoryCollection source, MyEntitiesValidationReport report, Map metaDataMap) { + // FIXME use EntityMetaDataValidator and validate attributes separately metaDataMap.values().forEach(MetaValidationUtils::validateEntityMetaData); report = generateEntityValidationReport(source, report, metaDataMap); diff --git a/molgenis-data-platform/src/main/java/org/molgenis/data/platform/decorators/MolgenisRepositoryDecoratorFactory.java b/molgenis-data-platform/src/main/java/org/molgenis/data/platform/decorators/MolgenisRepositoryDecoratorFactory.java index 000fca99895..155f2f5bd69 100644 --- a/molgenis-data-platform/src/main/java/org/molgenis/data/platform/decorators/MolgenisRepositoryDecoratorFactory.java +++ b/molgenis-data-platform/src/main/java/org/molgenis/data/platform/decorators/MolgenisRepositoryDecoratorFactory.java @@ -29,6 +29,8 @@ import org.molgenis.data.validation.EntityAttributesValidator; import org.molgenis.data.validation.ExpressionValidator; import org.molgenis.data.validation.RepositoryValidationDecorator; +import org.molgenis.data.validation.meta.EntityMetaDataRepositoryValidationDecorator; +import org.molgenis.data.validation.meta.EntityMetaDataValidator; import org.molgenis.security.core.MolgenisPermissionService; import org.molgenis.security.owned.OwnedEntityRepositoryDecorator; import org.molgenis.util.EntityUtils; @@ -68,6 +70,7 @@ public class MolgenisRepositoryDecoratorFactory implements RepositoryDecoratorFa private final L2Cache l2Cache; private final TransactionInformation transactionInformation; private final MolgenisPermissionService permissionService; + private final EntityMetaDataValidator entityMetaDataValidator; @Autowired public MolgenisRepositoryDecoratorFactory(EntityManager entityManager, @@ -79,7 +82,7 @@ public MolgenisRepositoryDecoratorFactory(EntityManager entityManager, AttributeMetaDataFactory attrMetaFactory, PasswordEncoder passwordEncoder, EntityMetaDataMetaData entityMetaMeta, I18nStringMetaData i18nStringMeta, L1Cache l1Cache, L2Cache l2Cache, TransactionInformation transactionInformation, EntityListenersService entityListenersService, - MolgenisPermissionService permissionService) + MolgenisPermissionService permissionService, EntityMetaDataValidator entityMetaDataValidator) { this.entityManager = requireNonNull(entityManager); @@ -102,6 +105,7 @@ public MolgenisRepositoryDecoratorFactory(EntityManager entityManager, this.l2Cache = requireNonNull(l2Cache); this.transactionInformation = requireNonNull(transactionInformation); this.permissionService = requireNonNull(permissionService); + this.entityMetaDataValidator = requireNonNull(entityMetaDataValidator); } @Override @@ -171,6 +175,9 @@ else if (repo.getName().equals(ATTRIBUTE_META_DATA)) } else if (repo.getName().equals(ENTITY_META_DATA)) { + repo = (Repository) (Repository) new EntityMetaDataRepositoryValidationDecorator( + (Repository) (Repository) repo, entityMetaDataValidator); + repo = (Repository) (Repository) new EntityMetaDataRepositoryDecorator( (Repository) (Repository) repo, dataService, systemEntityMetaDataRegistry, permissionService); diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/ConstraintViolation.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/ConstraintViolation.java index 94839d289f3..e1de41152f8 100644 --- a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/ConstraintViolation.java +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/ConstraintViolation.java @@ -18,6 +18,11 @@ public class ConstraintViolation private String importInfo; private Long rownr; + public ConstraintViolation(String message) + { + this(message, null); + } + public ConstraintViolation(String message, Long rownr) { this.message = message; diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/MolgenisValidationException.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/MolgenisValidationException.java index 6f82d4fed9e..f14226141e0 100644 --- a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/MolgenisValidationException.java +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/MolgenisValidationException.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; import org.molgenis.data.MolgenisDataException; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -13,6 +14,11 @@ public class MolgenisValidationException extends MolgenisDataException private static final long serialVersionUID = 1L; private Set violations; + public MolgenisValidationException(ConstraintViolation violation) + { + this(Collections.singleton(violation)); + } + public MolgenisValidationException(Set violations) { this.violations = violations; diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecorator.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecorator.java new file mode 100644 index 00000000000..a1d5a53afb8 --- /dev/null +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecorator.java @@ -0,0 +1,65 @@ +package org.molgenis.data.validation.meta; + +import org.molgenis.data.AbstractRepositoryDecorator; +import org.molgenis.data.Repository; +import org.molgenis.data.meta.model.EntityMetaData; + +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +/** + * Validates entity meta before adding or updating the delegated repository + */ +public class EntityMetaDataRepositoryValidationDecorator extends AbstractRepositoryDecorator +{ + private final Repository decoratedRepo; + private final EntityMetaDataValidator entityMetaDataValidator; + + public EntityMetaDataRepositoryValidationDecorator(Repository decoratedRepo, + EntityMetaDataValidator entityMetaDataValidator) + { + this.decoratedRepo = requireNonNull(decoratedRepo); + this.entityMetaDataValidator = requireNonNull(entityMetaDataValidator); + } + + @Override + public Repository delegate() + { + return decoratedRepo; + } + + @Override + public void update(EntityMetaData entityMeta) + { + entityMetaDataValidator.validate(entityMeta); + decoratedRepo.update(entityMeta); + } + + @Override + public void update(Stream entities) + { + decoratedRepo.update(entities.filter(entityMeta -> + { + entityMetaDataValidator.validate(entityMeta); + return true; + })); + } + + @Override + public void add(EntityMetaData entityMeta) + { + entityMetaDataValidator.validate(entityMeta); + decoratedRepo.add(entityMeta); + } + + @Override + public Integer add(Stream entities) + { + return decoratedRepo.add(entities.filter(entityMeta -> + { + entityMetaDataValidator.validate(entityMeta); + return true; + })); + } +} diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java new file mode 100644 index 00000000000..712c414180c --- /dev/null +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java @@ -0,0 +1,212 @@ +package org.molgenis.data.validation.meta; + +import org.molgenis.data.DataService; +import org.molgenis.data.MolgenisDataException; +import org.molgenis.data.RepositoryCollection; +import org.molgenis.data.meta.model.AttributeMetaData; +import org.molgenis.data.meta.model.EntityMetaData; +import org.molgenis.data.meta.model.Package; +import org.molgenis.data.support.AttributeMetaDataUtils; +import org.molgenis.data.validation.ConstraintViolation; +import org.molgenis.data.validation.MolgenisValidationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.StreamSupport.stream; +import static org.molgenis.data.meta.MetaValidationUtils.validateName; +import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.ATTRIBUTE_META_DATA; +import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.PARTS; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ATTRIBUTES; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ENTITY_META_DATA; +import static org.molgenis.data.meta.model.PackageMetaData.PACKAGE; + +@Component +public class EntityMetaDataValidator +{ + private final DataService dataService; + + @Autowired + public EntityMetaDataValidator(DataService dataService) + { + this.dataService = requireNonNull(dataService); + } + + public void validate(EntityMetaData entityMeta) + { + // validate entity name (e.g. illegal characters, length) + String name = entityMeta.getName(); + if (!name.equals(ATTRIBUTE_META_DATA) && !name.equals(ENTITY_META_DATA) && !name.equals(PACKAGE)) + { + try + { + validateName(entityMeta.getSimpleName()); + } + catch (MolgenisDataException e) + { + throw new MolgenisValidationException(new ConstraintViolation(e.getMessage())); + } + } + + // Validate that entity name equals entity package name + underscore + entity simple name + Package package_ = entityMeta.getPackage(); + if (package_ != null) + { + if (!(package_.getName() + '_' + entityMeta.getSimpleName()).equals(entityMeta.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Qualified entity name [%s] not equal to entity package name [%s] underscore entity name [%s]", + entityMeta.getName(), package_.getName(), entityMeta.getSimpleName()))); + } + } + else + { + if (!entityMeta.getSimpleName().equals(entityMeta.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Qualified entity name [%s] not equal to entity name [%s]", entityMeta.getName(), + entityMeta.getSimpleName()))); + } + } + + // Validate that entity attributes are not owned by another entity + entityMeta.getOwnAllAttributes().forEach(attr -> + { + EntityMetaData ownerEntityMeta = getAttributeOwner(attr); + if (ownerEntityMeta != null && !ownerEntityMeta.getName().equals(entityMeta.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Attribute [%s] is owned by entity [%s]", attr.getName(), ownerEntityMeta.getName()))); + } + }); + + Map ownAllAttrMap = stream(entityMeta.getOwnAllAttributes().spliterator(), false) + .collect(toMap(AttributeMetaData::getIdentifier, Function.identity(), (u, v) -> + { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, LinkedHashMap::new)); + + // Validate that entity attributes with same name do no exist in parent entity + EntityMetaData extendsEntityMeta = entityMeta.getExtends(); + if (extendsEntityMeta != null) + { + Map extendsAllAttrMap = stream( + extendsEntityMeta.getAllAttributes().spliterator(), false) + .collect(toMap(AttributeMetaData::getName, Function.identity(), (u, v) -> + { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, LinkedHashMap::new)); + + entityMeta.getOwnAllAttributes().forEach(attr -> + { + if (extendsAllAttrMap.containsKey(attr.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("An attribute with name [%s] already exists in entity [%s] or one of its parents", + attr.getName(), extendsEntityMeta.getName()))); + } + }); + } + + // Validate ID attribute + AttributeMetaData ownIdAttr = entityMeta.getOwnIdAttribute(); + if (ownIdAttr != null) + { + // Validate that ID attribute is in the attributes list + AttributeMetaData ownAttr = ownAllAttrMap.get(ownIdAttr.getIdentifier()); + if (ownAttr == null) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("ID attribute [%s] is not part of the entity attributes", ownIdAttr.getName()))); + } + + // Validate that ID attribute data type is allowed + if (!AttributeMetaDataUtils.isIdAttributeTypeAllowed(ownIdAttr)) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("ID attribute [%s] type [%s] is not allowed", ownIdAttr.getName(), + ownIdAttr.getDataType().toString()))); + } + + // Validate that ID attribute is unique + if (!ownIdAttr.isUnique()) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("ID attribute [%s] is not a unique attribute", ownIdAttr.getName()))); + } + + // Validate that ID attribute is not nillable + if (ownIdAttr.isNillable()) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("ID attribute [%s] is not a non-nillable attribute", ownIdAttr.getName()))); + } + } + else + { + if (!entityMeta.isAbstract() && entityMeta.getIdAttribute() == null) + { + throw new MolgenisValidationException(new ConstraintViolation("Missing required ID attribute")); + } + } + + // Validate label attribute + AttributeMetaData ownLabelAttr = entityMeta.getOwnLabelAttribute(); + if (ownLabelAttr != null) + { + // Validate that label attribute is in the attributes list + AttributeMetaData ownAttr = ownAllAttrMap.get(ownLabelAttr.getIdentifier()); + if (ownAttr == null) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Label attribute [%s] is not part of the entity attributes", ownLabelAttr.getName()))); + } + } + + // Validate lookup attributes + entityMeta.getOwnLookupAttributes().forEach(ownLookupAttr -> + { + // Validate that lookup attribute is in the attributes list + AttributeMetaData ownAttr = ownAllAttrMap.get(ownLookupAttr.getIdentifier()); + if (ownAttr == null) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Lookup attribute [%s] is not part of the entity attributes", ownLookupAttr.getName()))); + } + }); + + // Validate backend exists + String backendName = entityMeta.getBackend(); + RepositoryCollection repoCollection = dataService.getMeta().getBackend(backendName); + if (repoCollection == null) + { + throw new MolgenisValidationException(new ConstraintViolation(format("Unknown backend [%s]", backendName))); + } + } + + private EntityMetaData getAttributeOwner(AttributeMetaData attr) + { + EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(ATTRIBUTES, attr) + .findOne(); + if (entityMeta == null) + { + AttributeMetaData parentAttr = dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class) + .eq(PARTS, attr).findOne(); + if (parentAttr != null) + { + entityMeta = getAttributeOwner(parentAttr); + } + else + { + entityMeta = null; + } + } + return entityMeta; + } +} diff --git a/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecoratorTest.java b/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecoratorTest.java new file mode 100644 index 00000000000..bab8087e582 --- /dev/null +++ b/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataRepositoryValidationDecoratorTest.java @@ -0,0 +1,130 @@ +package org.molgenis.data.validation.meta; + +import org.mockito.ArgumentCaptor; +import org.molgenis.data.Repository; +import org.molgenis.data.meta.model.EntityMetaData; +import org.molgenis.data.validation.MolgenisValidationException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.stream.Stream; + +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertEquals; + +public class EntityMetaDataRepositoryValidationDecoratorTest +{ + private EntityMetaDataRepositoryValidationDecorator entityMetaRepoValidationDecorator; + private Repository decoratedRepo; + private EntityMetaDataValidator entityMetaDataValidator; + + @BeforeMethod + public void setUpBeforeMethod() + { + //noinspection unchecked + decoratedRepo = mock(Repository.class); + entityMetaDataValidator = mock(EntityMetaDataValidator.class); + entityMetaRepoValidationDecorator = new EntityMetaDataRepositoryValidationDecorator(decoratedRepo, + entityMetaDataValidator); + } + + @Test(expectedExceptions = NullPointerException.class) + public void EntityMetaDataRepositoryValidationDecorator() + { + new EntityMetaDataRepositoryValidationDecorator(null, null); + } + + @Test + public void delegate() + { + assertEquals(entityMetaRepoValidationDecorator.delegate(), decoratedRepo); + } + + @Test + public void updateEntityValid() + { + EntityMetaData entityMeta = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta); + entityMetaRepoValidationDecorator.update(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class) + public void updateEntityInvalid() throws Exception + { + EntityMetaData entityMeta = mock(EntityMetaData.class); + doThrow(mock(MolgenisValidationException.class)).when(entityMetaDataValidator).validate(entityMeta); + entityMetaRepoValidationDecorator.update(entityMeta); + } + + @Test + public void updateEntityStreamValid() + { + EntityMetaData entityMeta0 = mock(EntityMetaData.class); + EntityMetaData entityMeta1 = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta0); + doNothing().when(entityMetaDataValidator).validate(entityMeta1); + entityMetaRepoValidationDecorator.update(Stream.of(entityMeta0, entityMeta1)); + //noinspection unchecked + ArgumentCaptor> captor = ArgumentCaptor.forClass((Class) Stream.class); + verify(decoratedRepo).update(captor.capture()); + captor.getValue().count(); // process all entities in stream + } + + @Test(expectedExceptions = MolgenisValidationException.class) + public void updateEntityStreamInvalid() + { + EntityMetaData entityMeta0 = mock(EntityMetaData.class); + EntityMetaData entityMeta1 = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta0); + doThrow(mock(MolgenisValidationException.class)).when(entityMetaDataValidator).validate(entityMeta1); + entityMetaRepoValidationDecorator.update(Stream.of(entityMeta0, entityMeta1)); + //noinspection unchecked + ArgumentCaptor> captor = ArgumentCaptor.forClass((Class) Stream.class); + verify(decoratedRepo).update(captor.capture()); + captor.getValue().count(); // process all entities in stream + } + + @Test + public void addEntityValid() + { + EntityMetaData entityMeta = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta); + entityMetaRepoValidationDecorator.add(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class) + public void addEntityInvalid() + { + EntityMetaData entityMeta = mock(EntityMetaData.class); + doThrow(mock(MolgenisValidationException.class)).when(entityMetaDataValidator).validate(entityMeta); + entityMetaRepoValidationDecorator.add(entityMeta); + } + + @Test + public void addEntityStreamValid() + { + EntityMetaData entityMeta0 = mock(EntityMetaData.class); + EntityMetaData entityMeta1 = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta0); + doNothing().when(entityMetaDataValidator).validate(entityMeta1); + entityMetaRepoValidationDecorator.add(Stream.of(entityMeta0, entityMeta1)); + //noinspection unchecked + ArgumentCaptor> captor = ArgumentCaptor.forClass((Class) Stream.class); + verify(decoratedRepo).add(captor.capture()); + captor.getValue().count(); // process all entities in stream + } + + @Test(expectedExceptions = MolgenisValidationException.class) + public void addEntityStreamInvalid() + { + EntityMetaData entityMeta0 = mock(EntityMetaData.class); + EntityMetaData entityMeta1 = mock(EntityMetaData.class); + doNothing().when(entityMetaDataValidator).validate(entityMeta0); + doThrow(mock(MolgenisValidationException.class)).when(entityMetaDataValidator).validate(entityMeta1); + entityMetaRepoValidationDecorator.add(Stream.of(entityMeta0, entityMeta1)); + //noinspection unchecked + ArgumentCaptor> captor = ArgumentCaptor.forClass((Class) Stream.class); + verify(decoratedRepo).add(captor.capture()); + captor.getValue().count(); // process all entities in stream + } +} \ No newline at end of file diff --git a/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataValidatorTest.java b/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataValidatorTest.java new file mode 100644 index 00000000000..add25b956c6 --- /dev/null +++ b/molgenis-data-validation/src/test/java/org/molgenis/data/validation/meta/EntityMetaDataValidatorTest.java @@ -0,0 +1,306 @@ +package org.molgenis.data.validation.meta; + +import org.molgenis.data.DataService; +import org.molgenis.data.Query; +import org.molgenis.data.RepositoryCollection; +import org.molgenis.data.meta.MetaDataService; +import org.molgenis.data.meta.model.AttributeMetaData; +import org.molgenis.data.meta.model.EntityMetaData; +import org.molgenis.data.meta.model.Package; +import org.molgenis.data.validation.MolgenisValidationException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.molgenis.MolgenisFieldTypes.AttributeType.STRING; +import static org.molgenis.MolgenisFieldTypes.AttributeType.XREF; +import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.ATTRIBUTE_META_DATA; +import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.PARTS; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ATTRIBUTES; +import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ENTITY_META_DATA; + +public class EntityMetaDataValidatorTest +{ + private EntityMetaDataValidator entityMetaDataValidator; + private DataService dataService; + + private EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + private AttributeMetaData idAttr; + private AttributeMetaData labelAttr; + private Query entityQ; + private Query attrQ; + + @BeforeMethod + public void setUpBeforeMethod() + { + dataService = mock(DataService.class); + MetaDataService metaDataService = mock(MetaDataService.class); + when(dataService.getMeta()).thenReturn(metaDataService); + entityMetaDataValidator = new EntityMetaDataValidator(dataService); + + String backendName = "backend"; + RepositoryCollection repoCollection = mock(RepositoryCollection.class); + when(metaDataService.getBackend(backendName)).thenReturn(repoCollection); + + // valid entity meta + entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + + idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("idAttr").getMock(); + when(idAttr.getIdentifier()).thenReturn("#idAttr"); + when(idAttr.getDataType()).thenReturn(STRING); + when(idAttr.isUnique()).thenReturn(true); + when(idAttr.isNillable()).thenReturn(false); + labelAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("labelAttr").getMock(); + when(labelAttr.getIdentifier()).thenReturn("#labelAttr"); + when(labelAttr.getDataType()).thenReturn(STRING); + + //noinspection unchecked + entityQ = mock(Query.class); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + //noinspection unchecked + Query entityQ0 = mock(Query.class); + //noinspection unchecked + Query entityQ1 = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, idAttr)).thenReturn(entityQ0); + when(entityQ.eq(ATTRIBUTES, labelAttr)).thenReturn(entityQ1); + when(entityQ0.findOne()).thenReturn(null); + when(entityQ1.findOne()).thenReturn(null); + + //noinspection unchecked + attrQ = mock(Query.class); + //noinspection unchecked + Query attrQ0 = mock(Query.class); + //noinspection unchecked + Query attrQ1 = mock(Query.class); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, idAttr)).thenReturn(attrQ0); + when(attrQ.eq(PARTS, labelAttr)).thenReturn(attrQ1); + when(attrQ0.findOne()).thenReturn(null); + when(attrQ1.findOne()).thenReturn(null); + + String packageName = "package"; + Package package_ = when(mock(Package.class).getName()).thenReturn(packageName).getMock(); + when(entityMeta.getPackage()).thenReturn(package_); + String name = "name"; + when(entityMeta.getName()).thenReturn(packageName + '_' + name); + when(entityMeta.getSimpleName()).thenReturn(name); + when(entityMeta.getOwnAllAttributes()).thenReturn(newArrayList(idAttr, labelAttr)); + when(entityMeta.getOwnIdAttribute()).thenReturn(idAttr); + when(entityMeta.getOwnLabelAttribute()).thenReturn(labelAttr); + when(entityMeta.getOwnLookupAttributes()).thenReturn(singletonList(labelAttr)); + when(entityMeta.isAbstract()).thenReturn(false); + when(entityMeta.getExtends()).thenReturn(null); + when(entityMeta.getBackend()).thenReturn(backendName); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Name \\[abstract\\] is not allowed because it is a reserved keyword.") + public void testValidateNameIsReservedKeyword() throws Exception + { + when(entityMeta.getSimpleName()).thenReturn("abstract"); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Attribute name \\[attributeWithNameExceedingMaxSize\\] is too long: maximum length is 30 characters.") + public void testValidateNameIsTooLong() throws Exception + { + when(entityMeta.getSimpleName()).thenReturn("attributeWithNameExceedingMaxSize"); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Qualified entity name \\[package_name\\] not equal to entity package name \\[package\\] underscore entity name \\[invalidName\\]") + public void testValidateFullNameDoesNotMatchPackageAndSimpleName() + { + when(entityMeta.getSimpleName()).thenReturn("invalidName"); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Qualified entity name \\[name\\] not equal to entity name \\[invalidName\\]") + public void testValidateFullNameDoesNotMatchSimpleName() + { + when(entityMeta.getPackage()).thenReturn(null); + when(entityMeta.getName()).thenReturn("name"); + when(entityMeta.getSimpleName()).thenReturn("invalidName"); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Attribute \\[labelAttr\\] is owned by entity \\[ownerEntity\\]") + public void testValidateAttributeOwnedByOtherEntity() + { + //noinspection unchecked + Query entityQ0 = mock(Query.class); + //noinspection unchecked + Query entityQ1 = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, idAttr)).thenReturn(entityQ0); + when(entityQ.eq(ATTRIBUTES, labelAttr)).thenReturn(entityQ1); + when(entityQ0.findOne()).thenReturn(null); + EntityMetaData ownerEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("ownerEntity").getMock(); + when(entityQ1.findOne()).thenReturn(ownerEntityMeta); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, idAttr)).thenReturn(attrQ); + when(attrQ.findOne()).thenReturn(null); + entityMetaDataValidator.validate(entityMeta); + } + + @Test + public void testValidateAttributeOwnedBySameEntity() + { + //noinspection unchecked + Query entityQ0 = mock(Query.class); + //noinspection unchecked + Query entityQ1 = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, idAttr)).thenReturn(entityQ0); + when(entityQ.eq(ATTRIBUTES, labelAttr)).thenReturn(entityQ1); + when(entityQ0.findOne()).thenReturn(null); + when(entityQ1.findOne()).thenReturn(entityMeta); // same entity + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, idAttr)).thenReturn(attrQ); + when(attrQ.findOne()).thenReturn(null); + entityMetaDataValidator.validate(entityMeta); // should not throw an exception + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Attribute \\[labelAttr\\] is owned by entity \\[ownerEntity\\]") + public void testValidateAttributePartOwnedByOtherEntity() + { + when(entityQ.eq(ATTRIBUTES, idAttr)).thenReturn(entityQ); + when(entityQ.eq(ATTRIBUTES, labelAttr)).thenReturn(entityQ); + when(entityQ.findOne()).thenReturn(null); + //noinspection unchecked + Query attrQ0 = mock(Query.class); + //noinspection unchecked + Query attrQ1 = mock(Query.class); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, idAttr)).thenReturn(attrQ0); + when(attrQ.eq(PARTS, labelAttr)).thenReturn(attrQ1); + when(attrQ0.findOne()).thenReturn(null); + AttributeMetaData attrParent = when(mock(AttributeMetaData.class).getName()).thenReturn("attrParent").getMock(); + when(attrQ1.findOne()).thenReturn(attrParent); + //noinspection unchecked + Query entityQ0 = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, attrParent)).thenReturn(entityQ0); + EntityMetaData ownerEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("ownerEntity").getMock(); + when(entityQ0.findOne()).thenReturn(ownerEntityMeta); + entityMetaDataValidator.validate(entityMeta); + } + + @Test + public void testValidateAttributePartOwnedBySameEntity() + { + when(entityQ.eq(ATTRIBUTES, idAttr)).thenReturn(entityQ); + when(entityQ.eq(ATTRIBUTES, labelAttr)).thenReturn(entityQ); + when(entityQ.findOne()).thenReturn(null); + //noinspection unchecked + Query attrQ0 = mock(Query.class); + //noinspection unchecked + Query attrQ1 = mock(Query.class); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, idAttr)).thenReturn(attrQ0); + when(attrQ.eq(PARTS, labelAttr)).thenReturn(attrQ1); + when(attrQ0.findOne()).thenReturn(null); + AttributeMetaData attrParent = when(mock(AttributeMetaData.class).getName()).thenReturn("attrParent").getMock(); + when(attrQ1.findOne()).thenReturn(attrParent); + //noinspection unchecked + Query entityQ0 = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, attrParent)).thenReturn(entityQ0); + when(entityQ0.findOne()).thenReturn(entityMeta); + entityMetaDataValidator.validate(entityMeta); // should not throw an exception + } + + @Test + public void testValidateAttributeNotOwnedByExtendedEntity() + { + EntityMetaData extendsEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(extendsEntityMeta.getAllAttributes()).thenReturn(emptyList()); + when(entityMeta.getExtends()).thenReturn(extendsEntityMeta); + entityMetaDataValidator.validate(entityMeta); // should not throw an exception + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "An attribute with name \\[idAttr\\] already exists in entity \\[extendsEntity\\] or one of its parents") + public void testValidateAttributeOwnedByExtendedEntity() + { + EntityMetaData extendsEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("extendsEntity") + .getMock(); + when(extendsEntityMeta.getAllAttributes()).thenReturn(singletonList(idAttr)); + when(entityMeta.getExtends()).thenReturn(extendsEntityMeta); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "ID attribute \\[idAttr\\] is not part of the entity attributes") + public void testValidateOwnIdAttributeInAttributes() + { + when(entityMeta.getOwnAllAttributes()).thenReturn(singletonList(labelAttr)); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "ID attribute \\[idAttr\\] type \\[XREF\\] is not allowed") + public void testValidateOwnIdAttributeTypeAllowed() + { + when(idAttr.getDataType()).thenReturn(XREF); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "ID attribute \\[idAttr\\] is not a unique attribute") + public void testValidateOwnIdAttributeUnique() + { + when(idAttr.isUnique()).thenReturn(false); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "ID attribute \\[idAttr\\] is not a non-nillable attribute") + public void testValidateOwnIdAttributeNonNillable() + { + when(idAttr.isNillable()).thenReturn(true); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Missing required ID attribute") + public void testValidateOwnIdAttributeNullIdAttributeNull() + { + when(entityMeta.getOwnIdAttribute()).thenReturn(null); + when(entityMeta.getIdAttribute()).thenReturn(null); + entityMetaDataValidator.validate(entityMeta); + } + + @Test + public void testValidateOwnIdAttributeNullIdAttributeNullAbstract() + { + when(entityMeta.isAbstract()).thenReturn(true); + when(entityMeta.getOwnIdAttribute()).thenReturn(null); + when(entityMeta.getIdAttribute()).thenReturn(null); + entityMetaDataValidator.validate(entityMeta); // valid + } + + @Test + public void testValidateOwnIdAttributeNullIdAttributeNotNull() + { + when(entityMeta.getOwnIdAttribute()).thenReturn(null); + AttributeMetaData parentIdAttr = mock(AttributeMetaData.class); + when(entityMeta.getIdAttribute()).thenReturn(parentIdAttr); + entityMetaDataValidator.validate(entityMeta); // valid + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Label attribute \\[labelAttr\\] is not part of the entity attributes") + public void testValidateOwnLabelAttributeInAttributes() + { + when(entityMeta.getOwnAllAttributes()).thenReturn(singletonList(idAttr)); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Lookup attribute \\[labelAttr\\] is not part of the entity attributes") + public void testValidateOwnLookupAttributesInAttributes() + { + when(entityMeta.getOwnAllAttributes()).thenReturn(singletonList(idAttr)); + when(entityMeta.getOwnLabelAttribute()).thenReturn(null); + entityMetaDataValidator.validate(entityMeta); + } + + @Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Unknown backend \\[invalidBackend\\]") + public void testValidateBackend() + { + when(entityMeta.getBackend()).thenReturn("invalidBackend"); + entityMetaDataValidator.validate(entityMeta); + } +} \ No newline at end of file diff --git a/molgenis-data/src/main/java/org/molgenis/data/AbstractRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/AbstractRepositoryDecorator.java index 312dcc22715..be175a2db03 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/AbstractRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/AbstractRepositoryDecorator.java @@ -14,19 +14,19 @@ * Abstract superclass for {@link Repository} decorators that delegates everything to the * decorated repository. */ -public abstract class AbstractRepositoryDecorator extends ForwardingObject implements Repository +public abstract class AbstractRepositoryDecorator extends ForwardingObject implements Repository { @Override - protected abstract Repository delegate(); + protected abstract Repository delegate(); @Override - public Iterator iterator() + public Iterator iterator() { return delegate().iterator(); } @Override - public void forEachBatched(Fetch fetch, Consumer> consumer, int batchSize) + public void forEachBatched(Fetch fetch, Consumer> consumer, int batchSize) { delegate().forEachBatched(fetch, consumer, batchSize); } @@ -56,25 +56,25 @@ public long count() } @Override - public Query query() + public Query query() { return delegate().query(); } @Override - public long count(Query q) + public long count(Query q) { return delegate().count(q); } @Override - public Stream findAll(Query q) + public Stream findAll(Query q) { return delegate().findAll(q); } @Override - public Entity findOne(Query q) + public E findOne(Query q) { return delegate().findOne(q); } @@ -86,25 +86,25 @@ public AggregateResult aggregate(AggregateQuery aggregateQuery) } @Override - public Entity findOneById(Object id) + public E findOneById(Object id) { return delegate().findOneById(id); } @Override - public Entity findOneById(Object id, Fetch fetch) + public E findOneById(Object id, Fetch fetch) { return delegate().findOneById(id, fetch); } @Override - public Stream findAll(Stream ids) + public Stream findAll(Stream ids) { return delegate().findAll(ids); } @Override - public Stream findAll(Stream ids, Fetch fetch) + public Stream findAll(Stream ids, Fetch fetch) { return delegate().findAll(ids, fetch); } @@ -122,13 +122,13 @@ public Set getCapabilities() } @Override - public void update(Entity entity) + public void update(E entity) { delegate().update(entity); } @Override - public void delete(Entity entity) + public void delete(E entity) { delegate().delete(entity); } @@ -146,25 +146,25 @@ public void deleteAll() } @Override - public void add(Entity entity) + public void add(E entity) { delegate().add(entity); } @Override - public Integer add(Stream entities) + public Integer add(Stream entities) { return delegate().add(entities); } @Override - public void update(Stream entities) + public void update(Stream entities) { delegate().update(entities); } @Override - public void delete(Stream entities) + public void delete(Stream entities) { delegate().delete(entities); } diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java index 11c30967009..ebc3c379981 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java @@ -5,7 +5,6 @@ import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; -import org.molgenis.data.meta.model.Tag; import org.molgenis.data.meta.system.SystemEntityMetaDataRegistry; import org.molgenis.data.support.QueryImpl; import org.molgenis.security.core.MolgenisPermissionService; @@ -35,7 +34,6 @@ import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.ATTRIBUTE_META_DATA; import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.NAME; import static org.molgenis.data.meta.model.EntityMetaDataMetaData.*; -import static org.molgenis.data.meta.model.TagMetaData.TAG; import static org.molgenis.security.core.Permission.COUNT; import static org.molgenis.security.core.Permission.READ; import static org.molgenis.security.core.utils.SecurityUtils.currentUserIsSu; @@ -341,12 +339,7 @@ public Integer add(Stream entities) private void addEntityMetaData(EntityMetaData entityMetaData) { - validateAddAllowed(entityMetaData); - - if (entityMetaData.getBackend() == null) - { - entityMetaData.setBackend(dataService.getMeta().getDefaultBackend().getName()); - } + validatePermission(entityMetaData.getName(), Permission.WRITEMETA); // add row to entities table decoratedRepo.add(entityMetaData); @@ -361,22 +354,6 @@ private void addEntityMetaData(EntityMetaData entityMetaData) } } - private void validateAddAllowed(EntityMetaData entityMetaData) - { - String entityName = entityMetaData.getName(); - validatePermission(entityName, Permission.WRITEMETA); - - // TODO replace with exists() once Repository.exists has been implemented - EntityMetaData existingEntityMetaData = findOneById(entityName, new Fetch().field(FULL_NAME)); - if (existingEntityMetaData != null) - { - throw new MolgenisDataException(format("Adding existing entity meta data [%s] is not allowed", entityName)); - } - - // FIXME: Importer validates emd twice!! - MetaValidationUtils.validateEntityMetaData(entityMetaData); - } - private void updateEntity(EntityMetaData entityMeta) { validateUpdateAllowed(entityMeta); @@ -461,8 +438,6 @@ private void validateUpdateAllowed(EntityMetaData entityMetaData) { throw new MolgenisDataException(format("Updating system entity meta data [%s] is not allowed", entityName)); } - - MetaValidationUtils.validateEntityMetaData(entityMetaData); } private void deleteEntityMetaData(EntityMetaData entityMeta) @@ -507,12 +482,6 @@ private void deleteEntityAttributes(EntityMetaData entityMetaData) dataService.delete(ATTRIBUTE_META_DATA, allAttrs); } - private void deleteEntityTags(EntityMetaData entityMetaData) - { - Iterable tags = entityMetaData.getTags(); - dataService.delete(TAG, StreamSupport.stream(tags.spliterator(), false)); - } - private void deleteEntityRepository(EntityMetaData entityMetaData) { String backend = entityMetaData.getBackend(); diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/MetaValidationUtils.java b/molgenis-data/src/main/java/org/molgenis/data/meta/MetaValidationUtils.java index 4d9551eae39..f99fa342e91 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/MetaValidationUtils.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/MetaValidationUtils.java @@ -113,27 +113,31 @@ protected static void validateAttribute(AttributeMetaData amd) /** * Validates an entity and all of its attributes. + * + * @param entityMeta entity meta data to validate + * @throw MolgenisDataException if entity meta data is not valid */ - public static void validateEntityMetaData(EntityMetaData emd) + public static void validateEntityMetaData(EntityMetaData entityMeta) { try { - if (!emd.getName().equals(ATTRIBUTE_META_DATA) && !emd.getName().equals(ENTITY_META_DATA) && !emd.getName() - .equals(PACKAGE)) + if (!entityMeta.getName().equals(ATTRIBUTE_META_DATA) && !entityMeta.getName().equals(ENTITY_META_DATA) + && !entityMeta.getName().equals(PACKAGE)) { - validateName(emd.getSimpleName()); - validateAttributes(emd.getAttributes()); + validateName(entityMeta.getSimpleName()); + validateAttributes(entityMeta.getAttributes()); } - if (emd.getIdAttribute() != null && emd.getIdAttribute().getDefaultValue() != null) + if (entityMeta.getIdAttribute() != null && entityMeta.getIdAttribute().getDefaultValue() != null) { throw new MolgenisDataException( - "ID attribute " + emd.getIdAttribute().getName() + " cannot have default value"); + "ID attribute " + entityMeta.getIdAttribute().getName() + " cannot have default value"); } } catch (MolgenisDataException e) { - throw new MolgenisDataException("Validation error in entity [" + emd.getName() + "]: " + e.getMessage(), e); + throw new MolgenisDataException( + "Validation error in entity [" + entityMeta.getName() + "]: " + e.getMessage(), e); } } diff --git a/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryDecorator.java index 991db57ec2c..1c279465b6a 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/reindex/ReindexActionRepositoryDecorator.java @@ -16,7 +16,7 @@ /** * {@link Repository} decorator that registers changes with a {@link ReindexActionRegisterServiceImpl}. */ -public class ReindexActionRepositoryDecorator extends AbstractRepositoryDecorator +public class ReindexActionRepositoryDecorator extends AbstractRepositoryDecorator { private final ReindexActionRegisterService reindexActionRegisterService; private final Repository decorated; From 5d557dffd838c041b944b4b303c5c0a509bd9f4b Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 18 Aug 2016 12:53:06 +0200 Subject: [PATCH 38/49] Fix #5208 Attribute ref entity updates succeed if they shouldn't --- .../PostgreSqlRepositoryCollection.java | 95 ++++++++++++----- .../PostgreSqlRepositoryCollectionTest.java | 100 ++++++++++++++++++ 2 files changed, 169 insertions(+), 26 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index 2ffffe3868c..0701994d0dc 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -151,20 +151,7 @@ public void deleteRepository(EntityMetaData entityMeta) throw new UnknownRepositoryException(entityMeta.getName()); } - getPersistedAttributesMref(entityMeta).forEach(mrefAttr -> - { - String sqlDropJunctionTable = getSqlDropJunctionTable(entityMeta, mrefAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Dropping junction table for entity [{}] attribute [{}]", entityMeta.getName(), - mrefAttr.getName()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlDropJunctionTable); - } - } - jdbcTemplate.execute(sqlDropJunctionTable); - }); + getPersistedAttributesMref(entityMeta).forEach(mrefAttr -> dropJunctionTable(entityMeta, mrefAttr)); String sqlDropTable = getSqlDropTable(entityMeta); if (LOG.isDebugEnabled()) @@ -313,6 +300,13 @@ private void updateColumn(EntityMetaData entityMeta, AttributeMetaData attr, Att updateDataType(entityMeta, attr, updatedAttr); } + // ref entity changes + if (attr.getRefEntity() != null && updatedAttr.getRefEntity() != null && !attr.getRefEntity().getName() + .equals(updatedAttr.getRefEntity().getName())) + { + updateRefEntity(entityMeta, attr, updatedAttr); + } + // enum option changes if (!Objects.equals(attr.getEnumOptions(), updatedAttr.getEnumOptions())) { @@ -320,6 +314,34 @@ private void updateColumn(EntityMetaData entityMeta, AttributeMetaData attr, Att } } + /** + * Updates foreign keys based on referenced entity changes. + * + * @param entityMeta entity meta data + * @param attr current attribute + * @param updatedAttr updated attribute + */ + private void updateRefEntity(EntityMetaData entityMeta, AttributeMetaData attr, AttributeMetaData updatedAttr) + { + if (isSingleReferenceType(attr) && isSingleReferenceType(updatedAttr)) + { + dropForeignKey(entityMeta, attr); + + if (attr.getRefEntity().getIdAttribute().getDataType() != updatedAttr.getRefEntity().getIdAttribute() + .getDataType()) + { + updateColumnDataType(entityMeta, updatedAttr); + } + + createForeignKey(entityMeta, updatedAttr); + } + else if (isMultipleReferenceType(attr) && isMultipleReferenceType(updatedAttr)) + { + dropJunctionTable(entityMeta, attr); + createJunctionTable(entityMeta, updatedAttr); + } + } + /** * Updates check constraint based on enum value changes. * @@ -387,17 +409,7 @@ private void updateDataType(EntityMetaData entityMeta, AttributeMetaData attr, A dropForeignKey(entityMeta, attr); } - String sqlSetDataType = getSqlSetDataType(entityMeta, updatedAttr); - if (LOG.isDebugEnabled()) - { - LOG.debug("Changing data type of entity [{}] attribute [{}] from [{}] to [{}]", entityMeta.getName(), - attr.getName(), attr.getDataType().toString(), updatedAttr.getDataType().toString()); - if (LOG.isTraceEnabled()) - { - LOG.trace("SQL: {}", sqlSetDataType); - } - } - jdbcTemplate.execute(sqlSetDataType); + updateColumnDataType(entityMeta, updatedAttr); // add foreign key on data type updates such as STRING --> XREF if (!isReferenceType(attr) && isSingleReferenceType(updatedAttr)) @@ -421,7 +433,8 @@ private void updateUnique(EntityMetaData entityMeta, AttributeMetaData attr, Att if (idAttr != null && idAttr.getName().equals(attr.getName())) { throw new MolgenisDataException( - format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), entityMeta.getName())); + format("ID attribute [%s] of entity [%s] must be unique", attr.getName(), + entityMeta.getName())); } dropUniqueKey(entityMeta, updatedAttr); @@ -675,6 +688,21 @@ private void dropColumn(EntityMetaData entityMeta, AttributeMetaData attr) jdbcTemplate.execute(dropColumnSql); } + private void updateColumnDataType(EntityMetaData entityMeta, AttributeMetaData attr) + { + String sqlSetDataType = getSqlSetDataType(entityMeta, attr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Changing data type of entity [{}] attribute [{}] to [{}]", entityMeta.getName(), attr.getName(), + attr.getDataType().toString()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlSetDataType); + } + } + jdbcTemplate.execute(sqlSetDataType); + } + private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData attr) { String createJunctionTableSql = getSqlCreateJunctionTable(entityMeta, attr); @@ -688,4 +716,19 @@ private void createJunctionTable(EntityMetaData entityMeta, AttributeMetaData at } jdbcTemplate.execute(createJunctionTableSql); } + + private void dropJunctionTable(EntityMetaData entityMeta, AttributeMetaData mrefAttr) + { + String sqlDropJunctionTable = getSqlDropJunctionTable(entityMeta, mrefAttr); + if (LOG.isDebugEnabled()) + { + LOG.debug("Dropping junction table for entity [{}] attribute [{}]", entityMeta.getName(), + mrefAttr.getName()); + if (LOG.isTraceEnabled()) + { + LOG.trace("SQL: {}", sqlDropJunctionTable); + } + } + jdbcTemplate.execute(sqlDropJunctionTable); + } } \ No newline at end of file diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 80608f71389..10bc308cf84 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -398,6 +398,106 @@ public void updateAttributeDoesNotExist() postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); } + @Test + public void updateAttributeRefEntityXref() + { + AttributeMetaData refIdAttr0 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr0").getMock(); + when(refIdAttr0.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity0").getMock(); + when(refEntityMeta0.getIdAttribute()).thenReturn(refIdAttr0); + + AttributeMetaData refIdAttr1 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr1").getMock(); + when(refIdAttr1.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity1").getMock(); + when(refEntityMeta1.getIdAttribute()).thenReturn(refIdAttr1); + + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(XREF); + when(attr.getRefEntity()).thenReturn(refEntityMeta0); + + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(XREF); + when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta1); + + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(2)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_fkey\"", + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\")")); + } + + @Test + public void updateAttributeRefEntityXrefDifferentIdAttrType() + { + AttributeMetaData refIdAttr0 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr0").getMock(); + when(refIdAttr0.getDataType()).thenReturn(INT); + EntityMetaData refEntityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity0").getMock(); + when(refEntityMeta0.getIdAttribute()).thenReturn(refIdAttr0); + + AttributeMetaData refIdAttr1 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr1").getMock(); + when(refIdAttr1.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity1").getMock(); + when(refEntityMeta1.getIdAttribute()).thenReturn(refIdAttr1); + + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + String attrName = "attr"; + + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(XREF); + when(attr.getRefEntity()).thenReturn(refEntityMeta0); + + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(XREF); + when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta1); + + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(3)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("ALTER TABLE \"entity\" DROP CONSTRAINT \"entity_attr_fkey\"", + "ALTER TABLE \"entity\" ALTER COLUMN \"attr\" SET DATA TYPE character varying(255) USING \"attr\"::character varying(255)", + "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\")")); + } + + @Test + public void updateAttributeRefEntityMref() + { + AttributeMetaData refIdAttr0 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr0").getMock(); + when(refIdAttr0.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta0 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity0").getMock(); + when(refEntityMeta0.getIdAttribute()).thenReturn(refIdAttr0); + + AttributeMetaData refIdAttr1 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr1").getMock(); + when(refIdAttr1.getDataType()).thenReturn(STRING); + EntityMetaData refEntityMeta1 = when(mock(EntityMetaData.class).getName()).thenReturn("refEntity1").getMock(); + when(refEntityMeta1.getIdAttribute()).thenReturn(refIdAttr1); + + AttributeMetaData idAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("id").getMock(); + when(idAttr.getDataType()).thenReturn(STRING); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.getIdAttribute()).thenReturn(idAttr); + + String attrName = "attr"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(entityMeta.getAttribute(attrName)).thenReturn(attr); + when(attr.getDataType()).thenReturn(MREF); + when(attr.getRefEntity()).thenReturn(refEntityMeta0); + + AttributeMetaData updatedAttr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + when(updatedAttr.getDataType()).thenReturn(MREF); + when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta1); + + postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); + ArgumentCaptor captor = forClass(String.class); + verify(jdbcTemplate, times(2)).execute(captor.capture()); + assertEquals(captor.getAllValues(), newArrayList("DROP TABLE \"entity_attr\"", + "CREATE TABLE IF NOT EXISTS \"entity_attr\" (\"order\" INT,\"id\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"id\") REFERENCES \"entity\"(\"id\") ON DELETE CASCADE, FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\") ON DELETE CASCADE, UNIQUE (\"attr\",\"id\"), UNIQUE (\"order\",\"id\"))")); + } + @Test public void addAttribute() { From 02c30c584cefd1fbc06c77bc0a4796fc2bf4cd07 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 18 Aug 2016 16:27:19 +0200 Subject: [PATCH 39/49] Fix #5210 Attribute enum options illegal update raw exception --- .../PostgreSqlExceptionTranslator.java | 20 +++++++++++++++++++ .../PostgreSqlExceptionTranslatorTest.java | 13 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java index d635b267870..3f86803b070 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java @@ -86,6 +86,8 @@ private MolgenisDataException doTranslate(PSQLException pSqlException) return translateForeignKeyViolation(pSqlException); case "23505": // unique_violation return translateUniqueKeyViolation(pSqlException); + case "23514": // check_violation + return translateCheckConstraintViolation(pSqlException); default: return null; } @@ -251,4 +253,22 @@ MolgenisValidationException translateUniqueKeyViolation(PSQLException pSqlExcept } } } + + /** + * Package private for testability + * + * @param pSqlException PostgreSQL exception + * @return translated validation exception + */ + MolgenisValidationException translateCheckConstraintViolation(PSQLException pSqlException) + { + ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); + String tableName = serverErrorMessage.getTable(); + String constraintName = serverErrorMessage.getConstraint(); + // constraint name: __chk + String columnName = constraintName.substring(tableName.length() + 1, constraintName.length() - 4); + ConstraintViolation constraintViolation = new ConstraintViolation( + format("Unknown enum value for attribute '%s' of entity '%s'.", columnName, tableName), null); + return new MolgenisValidationException(singleton(constraintViolation)); + } } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java index 51df0aa10d5..8c579463f91 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslatorTest.java @@ -158,4 +158,17 @@ public void translateInvalidIntegerExceptionDateTime() .translateInvalidIntegerException(new PSQLException(serverErrorMessage)); assertEquals(e.getMessage(), "Value [str1] of this entity attribute is not of type [DATE_TIME]."); } + + @Test + public void translateCheckConstraintViolation() + { + DataSource dataSource = mock(DataSource.class); + PostgreSqlExceptionTranslator postgreSqlExceptionTranslator = new PostgreSqlExceptionTranslator(dataSource); + ServerErrorMessage serverErrorMessage = mock(ServerErrorMessage.class); + when(serverErrorMessage.getTable()).thenReturn("entity"); + when(serverErrorMessage.getConstraint()).thenReturn("entity_column_chk"); + MolgenisValidationException e = postgreSqlExceptionTranslator + .translateCheckConstraintViolation(new PSQLException(serverErrorMessage)); + assertEquals(e.getMessage(), "Unknown enum value for attribute 'column' of entity 'entity'."); + } } From 0b7a956be6fcee553e08a444d52ecf7e1967da69 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 07:52:17 +0200 Subject: [PATCH 40/49] Fix incorrect error message --- .../data/rest/service/RestService.java | 5 ++-- .../data/rest/service/RestServiceTest.java | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java index 580cb70560b..3bfb34c187b 100644 --- a/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java +++ b/molgenis-data-rest/src/main/java/org/molgenis/data/rest/service/RestService.java @@ -326,9 +326,8 @@ else if (paramValue instanceof String) catch (ParseException e) { throw new MolgenisDataException( - format("Attribute [%s] value [%s] does not match date format [%s] or [%s]", attr.getName(), - paramStrValue, MolgenisDateFormat.DATEFORMAT_DATE, - MolgenisDateFormat.DATEFORMAT_DATETIME)); + format("Attribute [%s] value [%s] does not match date format [%s]", attr.getName(), + paramStrValue, MolgenisDateFormat.DATEFORMAT_DATE)); } } else diff --git a/molgenis-data-rest/src/test/java/org/molgenis/data/rest/service/RestServiceTest.java b/molgenis-data-rest/src/test/java/org/molgenis/data/rest/service/RestServiceTest.java index c92f30f1493..7302e6fb05d 100644 --- a/molgenis-data-rest/src/test/java/org/molgenis/data/rest/service/RestServiceTest.java +++ b/molgenis-data-rest/src/test/java/org/molgenis/data/rest/service/RestServiceTest.java @@ -1,16 +1,15 @@ package org.molgenis.data.rest.service; -import org.molgenis.data.DataService; -import org.molgenis.data.Entity; -import org.molgenis.data.EntityManager; -import org.molgenis.data.IdGenerator; +import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.file.FileStore; import org.molgenis.file.model.FileMetaFactory; +import org.molgenis.util.MolgenisDateFormat; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.text.ParseException; import java.util.Arrays; import static java.util.Collections.emptyList; @@ -83,4 +82,21 @@ public void toEntityValueMrefToStringAttr() Object entityValue = restService.toEntityValue(attr, "0,1"); // string assertEquals(entityValue, Arrays.asList(entity0, entity1)); } + + @Test + public void toEntityDateStringValueValid() throws ParseException + { + AttributeMetaData dateAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("dateAttr").getMock(); + when(dateAttr.getDataType()).thenReturn(DATE); + assertEquals(restService.toEntityValue(dateAttr, "2000-12-31"), + MolgenisDateFormat.getDateFormat().parse("2000-12-31")); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Attribute \\[dateAttr\\] value \\[invalidDate\\] does not match date format \\[yyyy-MM-dd\\]") + public void toEntityDateStringValueInvalid() + { + AttributeMetaData dateAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("dateAttr").getMock(); + when(dateAttr.getDataType()).thenReturn(DATE); + restService.toEntityValue(dateAttr, "invalidDate"); + } } From a012cc7b2f1d56c627c79a228d2373e034b86992 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 09:17:15 +0200 Subject: [PATCH 41/49] Merge with molgenis/master Fix compilation error --- .../molgenis/data/postgresql/PostgreSqlExceptionTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java index 406387a3170..654cbe61df5 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlExceptionTranslator.java @@ -274,7 +274,7 @@ static MolgenisValidationException translateUniqueKeyViolation(PSQLException pSq * @param pSqlException PostgreSQL exception * @return translated validation exception */ - MolgenisValidationException translateCheckConstraintViolation(PSQLException pSqlException) + static MolgenisValidationException translateCheckConstraintViolation(PSQLException pSqlException) { ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage(); String tableName = serverErrorMessage.getTable(); From 3817e5713b7e56176bd030a4ce7fdb7a243f10ff Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 12:26:02 +0200 Subject: [PATCH 42/49] FIX remove attribute defined in parent AuthorityMetaData --- .../data/importer/ImportWizardControllerTest.java | 8 ++++---- .../java/org/molgenis/auth/GroupAuthorityMetaData.java | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/molgenis-data-import/src/test/java/org/molgenis/data/importer/ImportWizardControllerTest.java b/molgenis-data-import/src/test/java/org/molgenis/data/importer/ImportWizardControllerTest.java index ab1e6811b04..cdfa359211e 100644 --- a/molgenis-data-import/src/test/java/org/molgenis/data/importer/ImportWizardControllerTest.java +++ b/molgenis-data-import/src/test/java/org/molgenis/data/importer/ImportWizardControllerTest.java @@ -142,25 +142,25 @@ public void setUp() throws ParseException group1.setName("TestGroup"); Entity entity1 = groupAuthorityFactory.create("Entity1"); - entity1.set(GroupAuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY1"); + entity1.set(AuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY1"); entity1.set(GroupAuthorityMetaData.MOLGENIS_GROUP, group1); GroupAuthority authority1 = groupAuthorityFactory.create(); authority1.set(entity1); Entity entity2 = groupAuthorityFactory.create("Entity2"); - entity2.set(GroupAuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY2"); + entity2.set(AuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY2"); entity2.set(GroupAuthorityMetaData.MOLGENIS_GROUP, group1); GroupAuthority authority2 = groupAuthorityFactory.create(); authority2.set(entity2); Entity entity3 = groupAuthorityFactory.create("Entity3"); - entity3.set(GroupAuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY3"); + entity3.set(AuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY3"); entity3.set(GroupAuthorityMetaData.MOLGENIS_GROUP, group1); GroupAuthority authority3 = groupAuthorityFactory.create(); authority3.set(entity3); Entity entity4 = groupAuthorityFactory.create("Entity4"); - entity4.set(GroupAuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY4"); + entity4.set(AuthorityMetaData.ROLE, SecurityUtils.AUTHORITY_ENTITY_WRITEMETA_PREFIX + "ENTITY4"); entity4.set(GroupAuthorityMetaData.MOLGENIS_GROUP, group1); GroupAuthority authority4 = groupAuthorityFactory.create(); authority4.set(entity4); diff --git a/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java b/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java index 2406542b699..2787749b705 100644 --- a/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/auth/GroupAuthorityMetaData.java @@ -20,7 +20,6 @@ public class GroupAuthorityMetaData extends SystemEntityMetaData public static final String GROUP_AUTHORITY = PACKAGE_SECURITY + PACKAGE_SEPARATOR + SIMPLE_NAME; public static final String MOLGENIS_GROUP = "molgenisGroup"; - public static final String ROLE = "role"; public static final String ID = "id"; private final SecurityPackage securityPackage; From bf00446f1093df46d3cd3daa7d8ec682687279d0 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 12:26:49 +0200 Subject: [PATCH 43/49] Fix #5213 Cannot delete entity with compound attributes --- .../AttributeMetaDataRepositoryDecorator.java | 14 +- .../EntityMetaDataRepositoryDecorator.java | 32 ++- .../molgenis/util/SecurityDecoratorUtils.java | 7 +- ...ributeMetaDataRepositoryDecoratorTest.java | 91 ++++++++ ...EntityMetaDataRepositoryDecoratorTest.java | 200 +++++++++++++++++- 5 files changed, 311 insertions(+), 33 deletions(-) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java index b0e91a2c47e..35c463f42ca 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecorator.java @@ -4,7 +4,6 @@ import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; -import org.molgenis.data.meta.model.EntityMetaDataMetaData; import org.molgenis.data.meta.system.SystemEntityMetaDataRegistry; import org.molgenis.data.support.QueryImpl; import org.molgenis.security.core.MolgenisPermissionService; @@ -282,11 +281,10 @@ public void delete(AttributeMetaData attr) @Override public void delete(Stream attrs) { - decoratedRepo.delete(attrs.filter(attr -> - { - validateDeleteAllowed(attr); - return true; - })); + // The validateDeleteAllowed check if querying the table in which we are deleting. Since the decorated repo only + // guarantees that the attributes are deleted after the operation completes we have to delete the attributes one + // by one + attrs.forEach(this::delete); } @Override @@ -437,8 +435,8 @@ private void validateDeleteAllowed(AttributeMetaData attr) throw new MolgenisDataException( format("Deleting system entity attribute [%s] is not allowed", attr.getName())); } - EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class) - .eq(EntityMetaDataMetaData.ATTRIBUTES, attr).findOne(); + EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(ATTRIBUTES, attr) + .findOne(); if (entityMeta != null) { throw new MolgenisDataException( diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java index 11c30967009..e2160180082 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecorator.java @@ -2,6 +2,8 @@ import com.google.common.collect.Sets; import com.google.common.collect.TreeTraverser; +import org.molgenis.auth.GroupAuthority; +import org.molgenis.auth.UserAuthority; import org.molgenis.data.*; import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; @@ -11,7 +13,6 @@ import org.molgenis.security.core.MolgenisPermissionService; import org.molgenis.security.core.Permission; import org.molgenis.security.core.utils.SecurityUtils; -import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import java.io.IOException; @@ -264,35 +265,30 @@ public AggregateResult aggregate(AggregateQuery aggregateQuery) } } - @Transactional @Override public void update(EntityMetaData entity) { updateEntity(entity); } - @Transactional @Override public void update(Stream entities) { entities.forEach(this::updateEntity); } - @Transactional @Override public void delete(EntityMetaData entity) { deleteEntityMetaData(entity); } - @Transactional @Override public void delete(Stream entities) { entities.forEach(this::deleteEntityMetaData); } - @Transactional @Override public void deleteById(Object id) { @@ -305,28 +301,24 @@ public void deleteById(Object id) deleteEntityMetaData(entityMetaData); } - @Transactional @Override public void deleteAll(Stream ids) { findAll(ids).forEach(this::deleteEntityMetaData); } - @Transactional @Override public void deleteAll() { iterator().forEachRemaining(this::deleteEntityMetaData); } - @Transactional @Override public void add(EntityMetaData entity) { addEntityMetaData(entity); } - @Transactional @Override public Integer add(Stream entities) { @@ -502,7 +494,7 @@ private void deleteEntityAttributes(EntityMetaData entityMetaData) Iterable rootAttrs = entityMetaData.getOwnAttributes(); Stream allAttrs = StreamSupport.stream(rootAttrs.spliterator(), false).flatMap( attrEntity -> StreamSupport - .stream(new AttributeMetaDataTreeTraverser().postOrderTraversal(attrEntity).spliterator(), + .stream(new AttributeMetaDataTreeTraverser().preOrderTraversal(attrEntity).spliterator(), false)); dataService.delete(ATTRIBUTE_META_DATA, allAttrs); } @@ -516,8 +508,7 @@ private void deleteEntityTags(EntityMetaData entityMetaData) private void deleteEntityRepository(EntityMetaData entityMetaData) { String backend = entityMetaData.getBackend(); - dataService.getMeta().getBackend(backend) - .deleteRepository(entityMetaData); // FIXME call deleteRepo directly on metadataservice + dataService.getMeta().getBackend(backend).deleteRepository(entityMetaData); } private void deleteEntityPermissions(EntityMetaData entityMetaData) @@ -526,17 +517,18 @@ private void deleteEntityPermissions(EntityMetaData entityMetaData) List authorities = SecurityUtils.getEntityAuthorities(entityName); // User permissions - if (dataService.hasRepository(USER_AUTHORITY)) + List userPermissions = dataService.query(USER_AUTHORITY, UserAuthority.class) + .in(ROLE, authorities).findAll().collect(toList()); + if (!userPermissions.isEmpty()) { - Stream userPermissions = dataService.query(USER_AUTHORITY).in(ROLE, authorities).findAll(); - dataService.delete(USER_AUTHORITY, userPermissions); + dataService.delete(USER_AUTHORITY, userPermissions.stream()); } - // Group permissions - if (dataService.hasRepository(GROUP_AUTHORITY)) + List groupPermissions = dataService.query(GROUP_AUTHORITY, GroupAuthority.class) + .in(ROLE, authorities).findAll().collect(toList()); + if (!groupPermissions.isEmpty()) { - Stream groupPermissions = dataService.query(GROUP_AUTHORITY).in(ROLE, authorities).findAll(); - dataService.delete(GROUP_AUTHORITY, groupPermissions); + dataService.delete(GROUP_AUTHORITY, groupPermissions.stream()); } } diff --git a/molgenis-data/src/main/java/org/molgenis/util/SecurityDecoratorUtils.java b/molgenis-data/src/main/java/org/molgenis/util/SecurityDecoratorUtils.java index 3600be585fb..f7d00569628 100644 --- a/molgenis-data/src/main/java/org/molgenis/util/SecurityDecoratorUtils.java +++ b/molgenis-data/src/main/java/org/molgenis/util/SecurityDecoratorUtils.java @@ -3,6 +3,7 @@ import org.molgenis.data.MolgenisDataAccessException; import org.molgenis.security.core.Permission; +import static java.lang.String.format; import static org.molgenis.security.core.utils.SecurityUtils.currentUserHasRole; public class SecurityDecoratorUtils @@ -10,17 +11,17 @@ public class SecurityDecoratorUtils public static void validatePermission(String entityName, Permission permission) { - String role = String.format("ROLE_ENTITY_%s_%s", permission.toString(), entityName.toUpperCase()); + String role = format("ROLE_ENTITY_%s_%s", permission.toString(), entityName.toUpperCase()); if (!currentUserHasRole("ROLE_SU", "ROLE_SYSTEM", role)) { throw new MolgenisDataAccessException( - "No " + permission.toString() + " permission on entity " + entityName); + format("No [%s] permission on entity [%s]", permission.toString(), entityName)); } } public static boolean isPermissionValid(String entityName, Permission permission) { - String role = String.format("ROLE_ENTITY_%s_%s", permission.toString(), entityName.toUpperCase()); + String role = format("ROLE_ENTITY_%s_%s", permission.toString(), entityName.toUpperCase()); return currentUserHasRole("ROLE_SU", "ROLE_SYSTEM", role); } diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecoratorTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecoratorTest.java index 9e180d909be..d45e11dca8d 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecoratorTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/AttributeMetaDataRepositoryDecoratorTest.java @@ -787,6 +787,97 @@ public void aggregateUser() throws Exception repo.aggregate(aggregateQuery); } + @Test + public void delete() + { + String attrName = "attrName"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + String attrIdentifier = "id"; + when(attr.getIdentifier()).thenReturn(attrIdentifier); + when(systemEntityMetaRegistry.hasSystemAttributeMetaData(attrIdentifier)).thenReturn(false); + + //noinspection unchecked + Query entityQ = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, attr)).thenReturn(entityQ); + when(entityQ.findOne()).thenReturn(null); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + + //noinspection unchecked + Query attrQ = mock(Query.class); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, attr)).thenReturn(attrQ); + when(attrQ.findOne()).thenReturn(null); + + repo.delete(attr); + + verify(decoratedRepo).delete(attr); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Deleting system entity attribute \\[attrName\\] is not allowed") + public void deleteSystemAttribute() + { + String attrName = "attrName"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + String attrIdentifier = "id"; + when(attr.getIdentifier()).thenReturn(attrIdentifier); + when(systemEntityMetaRegistry.hasSystemAttributeMetaData(attrIdentifier)).thenReturn(true); + repo.delete(attr); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Deleting attribute \\[attrName\\] is not allowed, since it is referenced by entity \\[ownerEntity\\]") + public void deleteReferencedByEntity() + { + String attrName = "attrName"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + String attrIdentifier = "id"; + when(attr.getIdentifier()).thenReturn(attrIdentifier); + when(systemEntityMetaRegistry.hasSystemAttributeMetaData(attrIdentifier)).thenReturn(false); + //noinspection unchecked + Query entityQ = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, attr)).thenReturn(entityQ); + EntityMetaData ownerEntityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("ownerEntity").getMock(); + when(entityQ.findOne()).thenReturn(ownerEntityMeta); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + repo.delete(attr); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Deleting attribute \\[attrName\\] is not allowed, since it is referenced by attribute \\[ownerAttr\\]") + public void deleteReferencedByAttribute() + { + String attrName = "attrName"; + AttributeMetaData attr = when(mock(AttributeMetaData.class).getName()).thenReturn(attrName).getMock(); + String attrIdentifier = "id"; + when(attr.getIdentifier()).thenReturn(attrIdentifier); + when(systemEntityMetaRegistry.hasSystemAttributeMetaData(attrIdentifier)).thenReturn(false); + + //noinspection unchecked + Query entityQ = mock(Query.class); + when(entityQ.eq(ATTRIBUTES, attr)).thenReturn(entityQ); + when(entityQ.findOne()).thenReturn(null); + when(dataService.query(ENTITY_META_DATA, EntityMetaData.class)).thenReturn(entityQ); + + AttributeMetaData ownerAttr = when(mock(AttributeMetaData.class).getName()).thenReturn("ownerAttr").getMock(); + //noinspection unchecked + Query attrQ = mock(Query.class); + when(dataService.query(ATTRIBUTE_META_DATA, AttributeMetaData.class)).thenReturn(attrQ); + when(attrQ.eq(PARTS, attr)).thenReturn(attrQ); + when(attrQ.findOne()).thenReturn(ownerAttr); + + repo.delete(attr); + } + + @Test + public void deleteStream() + { + AttributeMetaDataRepositoryDecorator repoSpy = spy(repo); + doNothing().when(repoSpy).delete(any(AttributeMetaData.class)); + AttributeMetaData attr0 = mock(AttributeMetaData.class); + AttributeMetaData attr1 = mock(AttributeMetaData.class); + repoSpy.delete(Stream.of(attr0, attr1)); + verify(repoSpy).delete(attr0); + verify(repoSpy).delete(attr1); + } + private static void setSuAuthentication() { TestingAuthenticationToken authentication = new TestingAuthenticationToken("su", null, AUTHORITY_SU); diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java index ba5aac29ebe..8d2a3927c79 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java @@ -1,8 +1,11 @@ package org.molgenis.data.meta; import org.mockito.ArgumentCaptor; +import org.molgenis.auth.GroupAuthority; +import org.molgenis.auth.UserAuthority; import org.molgenis.data.*; import org.molgenis.data.QueryRule.Operator; +import org.molgenis.data.meta.model.AttributeMetaData; import org.molgenis.data.meta.model.EntityMetaData; import org.molgenis.data.meta.system.SystemEntityMetaDataRegistry; import org.molgenis.data.support.QueryImpl; @@ -22,9 +25,14 @@ import static java.util.Collections.*; import static java.util.stream.Collectors.toList; import static org.mockito.ArgumentCaptor.forClass; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; +import static org.molgenis.auth.AuthorityMetaData.ROLE; +import static org.molgenis.auth.GroupAuthorityMetaData.GROUP_AUTHORITY; +import static org.molgenis.auth.UserAuthorityMetaData.USER_AUTHORITY; import static org.molgenis.data.QueryRule.Operator.EQUALS; import static org.molgenis.data.RepositoryCapability.WRITABLE; +import static org.molgenis.data.meta.model.AttributeMetaDataMetaData.ATTRIBUTE_META_DATA; import static org.molgenis.security.core.Permission.COUNT; import static org.molgenis.security.core.Permission.READ; import static org.molgenis.security.core.runas.SystemSecurityToken.ROLE_SYSTEM; @@ -38,6 +46,7 @@ public class EntityMetaDataRepositoryDecoratorTest private EntityMetaDataRepositoryDecorator repo; private Repository decoratedRepo; private DataService dataService; + private MetaDataService metaDataService; private SystemEntityMetaDataRegistry systemEntityMetaRegistry; private MolgenisPermissionService permissionService; @@ -46,6 +55,8 @@ public void setUpBeforeMethod() { decoratedRepo = mock(Repository.class); dataService = mock(DataService.class); + metaDataService = mock(MetaDataService.class); + when(dataService.getMeta()).thenReturn(metaDataService); systemEntityMetaRegistry = mock(SystemEntityMetaDataRegistry.class); permissionService = mock(MolgenisPermissionService.class); repo = new EntityMetaDataRepositoryDecorator(decoratedRepo, dataService, systemEntityMetaRegistry, @@ -584,8 +595,7 @@ public void aggregateSystem() throws Exception aggregateSuOrSystem(); } - @Test - public void aggregateSuOrSystem() throws Exception + private void aggregateSuOrSystem() throws Exception { AggregateQuery aggregateQuery = mock(AggregateQuery.class); AggregateResult aggregateResult = mock(AggregateResult.class); @@ -601,6 +611,192 @@ public void aggregateUser() throws Exception repo.aggregate(aggregateQuery); } + @Test + public void deleteSu() + { + setSuAuthentication(); + deleteSuOrSystem(); + } + + @Test + public void deleteSystem() + { + setSystemAuthentication(); + deleteSuOrSystem(); + } + + private void deleteSuOrSystem() + { + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + AttributeMetaData attr0 = mock(AttributeMetaData.class); + when(attr0.getAttributeParts()).thenReturn(emptyList()); + AttributeMetaData attrCompound = mock(AttributeMetaData.class); + AttributeMetaData attr1a = mock(AttributeMetaData.class); + when(attr1a.getAttributeParts()).thenReturn(emptyList()); + AttributeMetaData attr1b = mock(AttributeMetaData.class); + when(attr1b.getAttributeParts()).thenReturn(emptyList()); + when(attrCompound.getAttributeParts()).thenReturn(newArrayList(attr1a, attr1b)); + when(entityMeta.getOwnAttributes()).thenReturn(newArrayList(attr0, attrCompound)); + + String backendName = "backend"; + when(entityMeta.getBackend()).thenReturn(backendName); + RepositoryCollection repoCollection = mock(RepositoryCollection.class); + when(metaDataService.getBackend(backendName)).thenReturn(repoCollection); + //noinspection unchecked + Query userAuthorityQ = mock(Query.class); + when(userAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(userAuthorityQ); + UserAuthority userAuthority = mock(UserAuthority.class); + when(userAuthorityQ.findAll()).thenReturn(singletonList(userAuthority).stream()); + when(dataService.query(USER_AUTHORITY, UserAuthority.class)).thenReturn(userAuthorityQ); + + //noinspection unchecked + Query groupAuthorityQ = mock(Query.class); + when(groupAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(groupAuthorityQ); + GroupAuthority groupAuthority = mock(GroupAuthority.class); + when(groupAuthorityQ.findAll()).thenReturn(singletonList(groupAuthority).stream()); + when(dataService.query(GROUP_AUTHORITY, GroupAuthority.class)).thenReturn(groupAuthorityQ); + + repo.delete(entityMeta); + + verify(decoratedRepo).delete(entityMeta); + verify(repoCollection).deleteRepository(entityMeta); + + //noinspection unchecked + ArgumentCaptor> userAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(USER_AUTHORITY), userAuthorityCaptor.capture()); + assertEquals(userAuthorityCaptor.getValue().collect(toList()), singletonList(userAuthority)); + + //noinspection unchecked + ArgumentCaptor> groupAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(GROUP_AUTHORITY), groupAuthorityCaptor.capture()); + assertEquals(groupAuthorityCaptor.getValue().collect(toList()), singletonList(groupAuthority)); + + //noinspection unchecked + ArgumentCaptor> attrCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(ATTRIBUTE_META_DATA), attrCaptor.capture()); + assertEquals(attrCaptor.getValue().collect(toList()), newArrayList(attr0, attrCompound, attr1a, attr1b)); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "No \\[WRITEMETA\\] permission on entity \\[entity\\]") + public void deleteUser() + { + setUserAuthentication(); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + repo.delete(entityMeta); + } + + @Test + public void deleteAbstract() + { + setSystemAuthentication(); + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn("entity").getMock(); + when(entityMeta.isAbstract()).thenReturn(true); + AttributeMetaData attr0 = mock(AttributeMetaData.class); + when(attr0.getAttributeParts()).thenReturn(emptyList()); + when(entityMeta.getOwnAttributes()).thenReturn(singletonList(attr0)); + + String backendName = "backend"; + when(entityMeta.getBackend()).thenReturn(backendName); + RepositoryCollection repoCollection = mock(RepositoryCollection.class); + when(metaDataService.getBackend(backendName)).thenReturn(repoCollection); + //noinspection unchecked + Query userAuthorityQ = mock(Query.class); + when(userAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(userAuthorityQ); + UserAuthority userAuthority = mock(UserAuthority.class); + when(userAuthorityQ.findAll()).thenReturn(singletonList(userAuthority).stream()); + when(dataService.query(USER_AUTHORITY, UserAuthority.class)).thenReturn(userAuthorityQ); + + //noinspection unchecked + Query groupAuthorityQ = mock(Query.class); + when(groupAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(groupAuthorityQ); + GroupAuthority groupAuthority = mock(GroupAuthority.class); + when(groupAuthorityQ.findAll()).thenReturn(singletonList(groupAuthority).stream()); + when(dataService.query(GROUP_AUTHORITY, GroupAuthority.class)).thenReturn(groupAuthorityQ); + + repo.delete(entityMeta); + + verify(decoratedRepo).delete(entityMeta); + verify(repoCollection, times(0)).deleteRepository(entityMeta); // entity is abstract + + //noinspection unchecked + ArgumentCaptor> userAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(USER_AUTHORITY), userAuthorityCaptor.capture()); + assertEquals(userAuthorityCaptor.getValue().collect(toList()), singletonList(userAuthority)); + + //noinspection unchecked + ArgumentCaptor> groupAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(GROUP_AUTHORITY), groupAuthorityCaptor.capture()); + assertEquals(groupAuthorityCaptor.getValue().collect(toList()), singletonList(groupAuthority)); + + //noinspection unchecked + ArgumentCaptor> attrCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(ATTRIBUTE_META_DATA), attrCaptor.capture()); + assertEquals(attrCaptor.getValue().collect(toList()), singletonList(attr0)); + } + + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Deleting system entity meta data \\[entity\\] is not allowed") + public void deleteSystemEntity() + { + setSystemAuthentication(); + String entityName = "entity"; + EntityMetaData entityMeta = when(mock(EntityMetaData.class).getName()).thenReturn(entityName).getMock(); + when(entityMeta.isAbstract()).thenReturn(true); + AttributeMetaData attr0 = mock(AttributeMetaData.class); + when(attr0.getAttributeParts()).thenReturn(emptyList()); + when(entityMeta.getOwnAttributes()).thenReturn(singletonList(attr0)); + when(systemEntityMetaRegistry.hasSystemEntityMetaData(entityName)).thenReturn(true); + + String backendName = "backend"; + when(entityMeta.getBackend()).thenReturn(backendName); + RepositoryCollection repoCollection = mock(RepositoryCollection.class); + when(metaDataService.getBackend(backendName)).thenReturn(repoCollection); + //noinspection unchecked + Query userAuthorityQ = mock(Query.class); + when(userAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(userAuthorityQ); + UserAuthority userAuthority = mock(UserAuthority.class); + when(userAuthorityQ.findAll()).thenReturn(singletonList(userAuthority).stream()); + when(dataService.query(USER_AUTHORITY, UserAuthority.class)).thenReturn(userAuthorityQ); + + //noinspection unchecked + Query groupAuthorityQ = mock(Query.class); + when(groupAuthorityQ.in(ROLE, + newArrayList("ROLE_ENTITY_READ_ENTITY", "ROLE_ENTITY_WRITE_ENTITY", "ROLE_ENTITY_COUNT_ENTITY", + "ROLE_ENTITY_NONE_ENTITY", "ROLE_ENTITY_WRITEMETA_ENTITY"))).thenReturn(groupAuthorityQ); + GroupAuthority groupAuthority = mock(GroupAuthority.class); + when(groupAuthorityQ.findAll()).thenReturn(singletonList(groupAuthority).stream()); + when(dataService.query(GROUP_AUTHORITY, GroupAuthority.class)).thenReturn(groupAuthorityQ); + + repo.delete(entityMeta); + + verify(decoratedRepo).delete(entityMeta); + verify(repoCollection, times(0)).deleteRepository(entityMeta); // entity is abstract + + //noinspection unchecked + ArgumentCaptor> userAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(USER_AUTHORITY), userAuthorityCaptor.capture()); + assertEquals(userAuthorityCaptor.getValue().collect(toList()), singletonList(userAuthority)); + + //noinspection unchecked + ArgumentCaptor> groupAuthorityCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(GROUP_AUTHORITY), groupAuthorityCaptor.capture()); + assertEquals(groupAuthorityCaptor.getValue().collect(toList()), singletonList(groupAuthority)); + + //noinspection unchecked + ArgumentCaptor> attrCaptor = ArgumentCaptor.forClass((Class) Stream.class); + verify(dataService).delete(eq(ATTRIBUTE_META_DATA), attrCaptor.capture()); + assertEquals(attrCaptor.getValue().collect(toList()), singletonList(attr0)); + } + private static void setSuAuthentication() { TestingAuthenticationToken authentication = new TestingAuthenticationToken("su", null, AUTHORITY_SU); From aa6ce465a056876b0aa16174c64b54491be497dc Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 13:10:05 +0200 Subject: [PATCH 44/49] Refactor: split validate method in method per validation type --- .../meta/EntityMetaDataValidator.java | 237 ++++++++++++------ 1 file changed, 157 insertions(+), 80 deletions(-) diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java index 712c414180c..dde791510f7 100644 --- a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java @@ -40,80 +40,94 @@ public EntityMetaDataValidator(DataService dataService) public void validate(EntityMetaData entityMeta) { - // validate entity name (e.g. illegal characters, length) - String name = entityMeta.getName(); - if (!name.equals(ATTRIBUTE_META_DATA) && !name.equals(ENTITY_META_DATA) && !name.equals(PACKAGE)) - { - try - { - validateName(entityMeta.getSimpleName()); - } - catch (MolgenisDataException e) - { - throw new MolgenisValidationException(new ConstraintViolation(e.getMessage())); - } - } + validateEntityName(entityMeta); + validateOwnAttributes(entityMeta); - // Validate that entity name equals entity package name + underscore + entity simple name - Package package_ = entityMeta.getPackage(); - if (package_ != null) - { - if (!(package_.getName() + '_' + entityMeta.getSimpleName()).equals(entityMeta.getName())) - { - throw new MolgenisValidationException(new ConstraintViolation( - format("Qualified entity name [%s] not equal to entity package name [%s] underscore entity name [%s]", - entityMeta.getName(), package_.getName(), entityMeta.getSimpleName()))); - } - } - else + Map ownAllAttrMap = stream(entityMeta.getOwnAllAttributes().spliterator(), false) + .collect(toMap(AttributeMetaData::getIdentifier, Function.identity(), (u, v) -> + { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, LinkedHashMap::new)); + + validateOwnIdAttribute(entityMeta, ownAllAttrMap); + validateOwnLabelAttribute(entityMeta, ownAllAttrMap); + validateOwnLookupAttributes(entityMeta, ownAllAttrMap); + validateBackend(entityMeta); + } + + /** + * Validate that the entity meta data backend exists + * + * @param entityMeta entity meta data + * @throws MolgenisValidationException if the entity meta data backend does not exist + */ + private void validateBackend(EntityMetaData entityMeta) + { + // Validate backend exists + String backendName = entityMeta.getBackend(); + RepositoryCollection repoCollection = dataService.getMeta().getBackend(backendName); + if (repoCollection == null) { - if (!entityMeta.getSimpleName().equals(entityMeta.getName())) - { - throw new MolgenisValidationException(new ConstraintViolation( - format("Qualified entity name [%s] not equal to entity name [%s]", entityMeta.getName(), - entityMeta.getSimpleName()))); - } + throw new MolgenisValidationException(new ConstraintViolation(format("Unknown backend [%s]", backendName))); } + } - // Validate that entity attributes are not owned by another entity - entityMeta.getOwnAllAttributes().forEach(attr -> + /** + * Validate that the lookup attributes owned by this entity are part of the owned attributes. + * + * @param entityMeta entity meta data + * @param ownAllAttrMap attribute identifier to attribute map + * @throws MolgenisValidationException if one or more lookup attributes are not entity attributes + */ + private static void validateOwnLookupAttributes(EntityMetaData entityMeta, + Map ownAllAttrMap) + { + // Validate lookup attributes + entityMeta.getOwnLookupAttributes().forEach(ownLookupAttr -> { - EntityMetaData ownerEntityMeta = getAttributeOwner(attr); - if (ownerEntityMeta != null && !ownerEntityMeta.getName().equals(entityMeta.getName())) + // Validate that lookup attribute is in the attributes list + AttributeMetaData ownAttr = ownAllAttrMap.get(ownLookupAttr.getIdentifier()); + if (ownAttr == null) { throw new MolgenisValidationException(new ConstraintViolation( - format("Attribute [%s] is owned by entity [%s]", attr.getName(), ownerEntityMeta.getName()))); + format("Lookup attribute [%s] is not part of the entity attributes", ownLookupAttr.getName()))); } }); + } - Map ownAllAttrMap = stream(entityMeta.getOwnAllAttributes().spliterator(), false) - .collect(toMap(AttributeMetaData::getIdentifier, Function.identity(), (u, v) -> - { - throw new IllegalStateException(String.format("Duplicate key %s", u)); - }, LinkedHashMap::new)); - - // Validate that entity attributes with same name do no exist in parent entity - EntityMetaData extendsEntityMeta = entityMeta.getExtends(); - if (extendsEntityMeta != null) + /** + * Validate that the label attribute owned by this entity is part of the owned attributes. + * + * @param entityMeta entity meta data + * @param ownAllAttrMap attribute identifier to attribute map + * @throws MolgenisValidationException if the label attribute is not an entity attribute + */ + private static void validateOwnLabelAttribute(EntityMetaData entityMeta, + Map ownAllAttrMap) + { + // Validate label attribute + AttributeMetaData ownLabelAttr = entityMeta.getOwnLabelAttribute(); + if (ownLabelAttr != null) { - Map extendsAllAttrMap = stream( - extendsEntityMeta.getAllAttributes().spliterator(), false) - .collect(toMap(AttributeMetaData::getName, Function.identity(), (u, v) -> - { - throw new IllegalStateException(String.format("Duplicate key %s", u)); - }, LinkedHashMap::new)); - - entityMeta.getOwnAllAttributes().forEach(attr -> + // Validate that label attribute is in the attributes list + AttributeMetaData ownAttr = ownAllAttrMap.get(ownLabelAttr.getIdentifier()); + if (ownAttr == null) { - if (extendsAllAttrMap.containsKey(attr.getName())) - { - throw new MolgenisValidationException(new ConstraintViolation( - format("An attribute with name [%s] already exists in entity [%s] or one of its parents", - attr.getName(), extendsEntityMeta.getName()))); - } - }); + throw new MolgenisValidationException(new ConstraintViolation( + format("Label attribute [%s] is not part of the entity attributes", ownLabelAttr.getName()))); + } } + } + /** + * Validate that the ID attribute owned by this entity is part of the owned attributes. + * + * @param entityMeta entity meta data + * @param ownAllAttrMap attribute identifier to attribute map + * @throws MolgenisValidationException if the ID attribute is not an entity attribute + */ + private static void validateOwnIdAttribute(EntityMetaData entityMeta, Map ownAllAttrMap) + { // Validate ID attribute AttributeMetaData ownIdAttr = entityMeta.getOwnIdAttribute(); if (ownIdAttr != null) @@ -155,41 +169,104 @@ public void validate(EntityMetaData entityMeta) throw new MolgenisValidationException(new ConstraintViolation("Missing required ID attribute")); } } + } - // Validate label attribute - AttributeMetaData ownLabelAttr = entityMeta.getOwnLabelAttribute(); - if (ownLabelAttr != null) + /** + * Validates the attributes owned by this entity: + * 1) validates that attributes are not owned by another entity + * 2) validates that the parent entity doesn't have entities with the same name + * + * @param entityMeta entity meta data + * @throws MolgenisValidationException if an attribute is owned by another entity or a parent attribute has the same name + */ + private void validateOwnAttributes(EntityMetaData entityMeta) + { + // Validate that entity attributes are not owned by another entity + entityMeta.getOwnAllAttributes().forEach(attr -> { - // Validate that label attribute is in the attributes list - AttributeMetaData ownAttr = ownAllAttrMap.get(ownLabelAttr.getIdentifier()); - if (ownAttr == null) + EntityMetaData ownerEntityMeta = getAttributeOwner(attr); + if (ownerEntityMeta != null && !ownerEntityMeta.getName().equals(entityMeta.getName())) { throw new MolgenisValidationException(new ConstraintViolation( - format("Label attribute [%s] is not part of the entity attributes", ownLabelAttr.getName()))); + format("Attribute [%s] is owned by entity [%s]", attr.getName(), ownerEntityMeta.getName()))); } + }); + + // Validate that entity attributes with same name do no exist in parent entity + EntityMetaData extendsEntityMeta = entityMeta.getExtends(); + if (extendsEntityMeta != null) + { + Map extendsAllAttrMap = stream( + extendsEntityMeta.getAllAttributes().spliterator(), false) + .collect(toMap(AttributeMetaData::getName, Function.identity(), (u, v) -> + { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, LinkedHashMap::new)); + + entityMeta.getOwnAllAttributes().forEach(attr -> + { + if (extendsAllAttrMap.containsKey(attr.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("An attribute with name [%s] already exists in entity [%s] or one of its parents", + attr.getName(), extendsEntityMeta.getName()))); + } + }); } + } - // Validate lookup attributes - entityMeta.getOwnLookupAttributes().forEach(ownLookupAttr -> + /** + * Validates the entity fully qualified name and simple name: + * - Validates that the entity simple name does not contain illegal characters and validates the name length + * - Validates that the fully qualified name, simple name and package name are consistent with each other + * + * @param entityMeta entity meta data + * @throws MolgenisValidationException if the entity simple name content is invalid or the fully qualified name, simple name and package name are not consistent + */ + private static void validateEntityName(EntityMetaData entityMeta) + { + // validate entity name (e.g. illegal characters, length) + String name = entityMeta.getName(); + if (!name.equals(ATTRIBUTE_META_DATA) && !name.equals(ENTITY_META_DATA) && !name.equals(PACKAGE)) { - // Validate that lookup attribute is in the attributes list - AttributeMetaData ownAttr = ownAllAttrMap.get(ownLookupAttr.getIdentifier()); - if (ownAttr == null) + try { - throw new MolgenisValidationException(new ConstraintViolation( - format("Lookup attribute [%s] is not part of the entity attributes", ownLookupAttr.getName()))); + validateName(entityMeta.getSimpleName()); } - }); + catch (MolgenisDataException e) + { + throw new MolgenisValidationException(new ConstraintViolation(e.getMessage())); + } + } - // Validate backend exists - String backendName = entityMeta.getBackend(); - RepositoryCollection repoCollection = dataService.getMeta().getBackend(backendName); - if (repoCollection == null) + // Validate that entity name equals entity package name + underscore + entity simple name + Package package_ = entityMeta.getPackage(); + if (package_ != null) { - throw new MolgenisValidationException(new ConstraintViolation(format("Unknown backend [%s]", backendName))); + if (!(package_.getName() + '_' + entityMeta.getSimpleName()).equals(entityMeta.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Qualified entity name [%s] not equal to entity package name [%s] underscore entity name [%s]", + entityMeta.getName(), package_.getName(), entityMeta.getSimpleName()))); + } + } + else + { + if (!entityMeta.getSimpleName().equals(entityMeta.getName())) + { + throw new MolgenisValidationException(new ConstraintViolation( + format("Qualified entity name [%s] not equal to entity name [%s]", entityMeta.getName(), + entityMeta.getSimpleName()))); + } } } + /** + * Returns the entity that owns the given attribute. + * + * @param attr attribute + * @return attribute owner + */ private EntityMetaData getAttributeOwner(AttributeMetaData attr) { EntityMetaData entityMeta = dataService.query(ENTITY_META_DATA, EntityMetaData.class).eq(ATTRIBUTES, attr) From d4672d0d5b5b9b0be53fe96ecee57690bf40d3d6 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 13:19:17 +0200 Subject: [PATCH 45/49] DOC update --- .../data/validation/meta/EntityMetaDataValidator.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java index dde791510f7..d72343accb8 100644 --- a/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java +++ b/molgenis-data-validation/src/main/java/org/molgenis/data/validation/meta/EntityMetaDataValidator.java @@ -27,6 +27,9 @@ import static org.molgenis.data.meta.model.EntityMetaDataMetaData.ENTITY_META_DATA; import static org.molgenis.data.meta.model.PackageMetaData.PACKAGE; +/** + * Entity meta data validator + */ @Component public class EntityMetaDataValidator { @@ -38,6 +41,12 @@ public EntityMetaDataValidator(DataService dataService) this.dataService = requireNonNull(dataService); } + /** + * Validates entity meta data + * + * @param entityMeta entity meta data + * @throws MolgenisValidationException if entity meta data is not valid + */ public void validate(EntityMetaData entityMeta) { validateEntityName(entityMeta); From fe8008744f5a9016957778d29be409578b58f7d4 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 13:23:03 +0200 Subject: [PATCH 46/49] FIX deleteUser unit test --- .../data/meta/EntityMetaDataRepositoryDecoratorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java index 8d2a3927c79..12daf437cf6 100644 --- a/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java +++ b/molgenis-data/src/test/java/org/molgenis/data/meta/EntityMetaDataRepositoryDecoratorTest.java @@ -681,7 +681,7 @@ private void deleteSuOrSystem() assertEquals(attrCaptor.getValue().collect(toList()), newArrayList(attr0, attrCompound, attr1a, attr1b)); } - @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "No \\[WRITEMETA\\] permission on entity \\[entity\\]") + @Test(expectedExceptions = MolgenisDataAccessException.class, expectedExceptionsMessageRegExp = "No \\[WRITEMETA\\] permission on entity \\[entity\\]") public void deleteUser() { setUserAuthentication(); From f3cff70ad4344ed1205868a6a9cb795db7454bcf Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 14:29:20 +0200 Subject: [PATCH 47/49] FIX do not allow refEntity change for (CATEGORICAL_)MREF instead of wiping existing data --- .../data/postgresql/PostgreSqlRepositoryCollection.java | 6 ++++-- .../data/postgresql/PostgreSqlRepositoryCollectionTest.java | 6 +----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java index cd101269427..29628fdd074 100644 --- a/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java +++ b/molgenis-data-postgresql/src/main/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollection.java @@ -337,8 +337,10 @@ private void updateRefEntity(EntityMetaData entityMeta, AttributeMetaData attr, } else if (isMultipleReferenceType(attr) && isMultipleReferenceType(updatedAttr)) { - dropJunctionTable(entityMeta, attr); - createJunctionTable(entityMeta, updatedAttr); + throw new MolgenisDataException( + format("Updating entity [%s] attribute [%s] referenced entity from [%s] to [%s] not allowed for type [%s]", + entityMeta.getName(), attr.getName(), attr.getRefEntity().getName(), + updatedAttr.getRefEntity().getName(), updatedAttr.getDataType().toString())); } } diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 10bc308cf84..7f364187819 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -463,7 +463,7 @@ public void updateAttributeRefEntityXrefDifferentIdAttrType() "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\")")); } - @Test + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Updating entity [entity] attribute [attr] referenced entity from [refEntity0] to [refEntity1] not allowed for type [MREF]") public void updateAttributeRefEntityMref() { AttributeMetaData refIdAttr0 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr0").getMock(); @@ -492,10 +492,6 @@ public void updateAttributeRefEntityMref() when(updatedAttr.getRefEntity()).thenReturn(refEntityMeta1); postgreSqlRepoCollection.updateAttribute(entityMeta, attr, updatedAttr); - ArgumentCaptor captor = forClass(String.class); - verify(jdbcTemplate, times(2)).execute(captor.capture()); - assertEquals(captor.getAllValues(), newArrayList("DROP TABLE \"entity_attr\"", - "CREATE TABLE IF NOT EXISTS \"entity_attr\" (\"order\" INT,\"id\" character varying(255) NOT NULL, \"attr\" character varying(255) NOT NULL, FOREIGN KEY (\"id\") REFERENCES \"entity\"(\"id\") ON DELETE CASCADE, FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\") ON DELETE CASCADE, UNIQUE (\"attr\",\"id\"), UNIQUE (\"order\",\"id\"))")); } @Test From 7adfa35f906c4ca5960ac9b11c969cd608fa9c81 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 14:31:12 +0200 Subject: [PATCH 48/49] Set EntityMetaDataMetaData.Abstract read-only --- .../org/molgenis/data/meta/model/EntityMetaDataMetaData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java index cc2b727129f..9998e1236e1 100644 --- a/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java +++ b/molgenis-data/src/main/java/org/molgenis/data/meta/model/EntityMetaDataMetaData.java @@ -57,6 +57,7 @@ public void init() addAttribute(LABEL_ATTRIBUTE).setDataType(XREF).setRefEntity(attrMetaMeta).setLabel("Label attribute"); addAttribute(LOOKUP_ATTRIBUTES).setDataType(MREF).setRefEntity(attrMetaMeta).setLabel("Lookup attributes"); addAttribute(ABSTRACT).setDataType(BOOL).setNillable(false).setReadOnly(true).setLabel("Abstract") + .setReadOnly(true) .setDefaultValue(FALSE.toString()); // TODO replace with autowired self-reference after update to Spring 4.3 addAttribute(EXTENDS).setDataType(XREF).setRefEntity(this).setReadOnly(true).setLabel("Extends"); From 4d43e99f9f5f249f42b66b7bdb794097e2699952 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 19 Aug 2016 14:54:33 +0200 Subject: [PATCH 49/49] Fix exception message --- .../data/postgresql/PostgreSqlRepositoryCollectionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java index 7f364187819..4cad7028342 100644 --- a/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java +++ b/molgenis-data-postgresql/src/test/java/org/molgenis/data/postgresql/PostgreSqlRepositoryCollectionTest.java @@ -463,7 +463,7 @@ public void updateAttributeRefEntityXrefDifferentIdAttrType() "ALTER TABLE \"entity\" ADD CONSTRAINT \"entity_attr_fkey\" FOREIGN KEY (\"attr\") REFERENCES \"refEntity1\"(\"refIdAttr1\")")); } - @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Updating entity [entity] attribute [attr] referenced entity from [refEntity0] to [refEntity1] not allowed for type [MREF]") + @Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Updating entity \\[entity\\] attribute \\[attr\\] referenced entity from \\[refEntity0\\] to \\[refEntity1\\] not allowed for type \\[MREF\\]") public void updateAttributeRefEntityMref() { AttributeMetaData refIdAttr0 = when(mock(AttributeMetaData.class).getName()).thenReturn("refIdAttr0").getMock();