org.apache.nifi.registry.db.DatabaseMetadataService Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.registry.db;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.extension.ExtensionFilterParams;
import org.apache.nifi.extension.manifest.ExtensionType;
import org.apache.nifi.extension.manifest.ProvidedServiceAPI;
import org.apache.nifi.registry.db.entity.BucketEntity;
import org.apache.nifi.registry.db.entity.BucketItemEntity;
import org.apache.nifi.registry.db.entity.BucketItemEntityType;
import org.apache.nifi.registry.db.entity.BundleEntity;
import org.apache.nifi.registry.db.entity.BundleVersionDependencyEntity;
import org.apache.nifi.registry.db.entity.BundleVersionEntity;
import org.apache.nifi.registry.db.entity.ExtensionAdditionalDetailsEntity;
import org.apache.nifi.registry.db.entity.ExtensionEntity;
import org.apache.nifi.registry.db.entity.ExtensionProvidedServiceApiEntity;
import org.apache.nifi.registry.db.entity.ExtensionRestrictionEntity;
import org.apache.nifi.registry.db.entity.FlowEntity;
import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
import org.apache.nifi.registry.db.entity.TagCountEntity;
import org.apache.nifi.registry.db.mapper.BucketEntityRowMapper;
import org.apache.nifi.registry.db.mapper.BucketItemEntityRowMapper;
import org.apache.nifi.registry.db.mapper.BundleEntityRowMapper;
import org.apache.nifi.registry.db.mapper.BundleVersionDependencyEntityRowMapper;
import org.apache.nifi.registry.db.mapper.BundleVersionEntityRowMapper;
import org.apache.nifi.registry.db.mapper.ExtensionEntityRowMapper;
import org.apache.nifi.registry.db.mapper.FlowEntityRowMapper;
import org.apache.nifi.registry.db.mapper.FlowSnapshotEntityRowMapper;
import org.apache.nifi.registry.db.mapper.TagCountEntityMapper;
import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.service.MetadataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class DatabaseMetadataService implements MetadataService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public DatabaseMetadataService(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//----------------- Buckets ---------------------------------
@Override
public BucketEntity createBucket(final BucketEntity b) {
final String sql = "INSERT INTO BUCKET (ID, NAME, DESCRIPTION, CREATED, ALLOW_EXTENSION_BUNDLE_REDEPLOY, ALLOW_PUBLIC_READ) VALUES (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sql,
b.getId(),
b.getName(),
b.getDescription(),
b.getCreated(),
b.isAllowExtensionBundleRedeploy() ? 1 : 0,
b.isAllowPublicRead() ? 1 : 0);
return b;
}
@Override
public BucketEntity getBucketById(final String bucketIdentifier) {
final String sql = "SELECT * FROM BUCKET WHERE id = ?";
try {
return jdbcTemplate.queryForObject(sql, new BucketEntityRowMapper(), bucketIdentifier);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public List getBucketsByName(final String name) {
final String sql = "SELECT * FROM BUCKET WHERE name = ? ORDER BY name ASC";
return jdbcTemplate.query(sql, new BucketEntityRowMapper(), name);
}
@Override
public BucketEntity updateBucket(final BucketEntity bucket) {
final String sql = "UPDATE BUCKET SET " +
"name = ?, " +
"description = ?, " +
"allow_extension_bundle_redeploy = ?, " +
"allow_public_read = ? " +
"WHERE id = ?";
jdbcTemplate.update(sql,
bucket.getName(),
bucket.getDescription(),
bucket.isAllowExtensionBundleRedeploy() ? 1 : 0,
bucket.isAllowPublicRead() ? 1 : 0,
bucket.getId());
return bucket;
}
@Override
public void deleteBucket(final BucketEntity bucket) {
// NOTE: Cascading deletes will delete from all child tables
final String sql = "DELETE FROM BUCKET WHERE id = ?";
jdbcTemplate.update(sql, bucket.getId());
}
@Override
public List getBuckets(final Set bucketIds) {
if (bucketIds == null || bucketIds.isEmpty()) {
return Collections.emptyList();
}
final StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM BUCKET WHERE ");
addIdentifiersInClause(sqlBuilder, "id", bucketIds);
sqlBuilder.append("ORDER BY name ASC");
return jdbcTemplate.query(sqlBuilder.toString(), new BucketEntityRowMapper(), bucketIds.toArray());
}
@Override
public List getAllBuckets() {
final String sql = "SELECT * FROM BUCKET ORDER BY name ASC";
return jdbcTemplate.query(sql, new BucketEntityRowMapper());
}
//----------------- BucketItems ---------------------------------
private static final String BASE_BUCKET_ITEMS_SQL =
"SELECT " +
"item.id as ID, " +
"item.name as NAME, " +
"item.description as DESCRIPTION, " +
"item.created as CREATED, " +
"item.modified as MODIFIED, " +
"item.item_type as ITEM_TYPE, " +
"b.id as BUCKET_ID, " +
"b.name as BUCKET_NAME ," +
"eb.bundle_type as BUNDLE_TYPE, " +
"eb.group_id as BUNDLE_GROUP_ID, " +
"eb.artifact_id as BUNDLE_ARTIFACT_ID " +
"FROM BUCKET_ITEM item " +
"INNER JOIN BUCKET b ON item.bucket_id = b.id " +
"LEFT JOIN BUNDLE eb ON item.id = eb.id ";
@Override
public List getBucketItems(final String bucketIdentifier) {
final String sql = BASE_BUCKET_ITEMS_SQL + " WHERE item.bucket_id = ?";
final List items = jdbcTemplate.query(sql, new BucketItemEntityRowMapper(), bucketIdentifier);
return getItemsWithCounts(items);
}
@Override
public List getBucketItems(final Set bucketIds) {
if (bucketIds == null || bucketIds.isEmpty()) {
return Collections.emptyList();
}
final StringBuilder sqlBuilder = new StringBuilder(BASE_BUCKET_ITEMS_SQL + " WHERE item.bucket_id IN (");
for (int i = 0; i < bucketIds.size(); i++) {
if (i > 0) {
sqlBuilder.append(", ");
}
sqlBuilder.append("?");
}
sqlBuilder.append(")");
final List items = jdbcTemplate.query(sqlBuilder.toString(), new BucketItemEntityRowMapper(), bucketIds.toArray());
return getItemsWithCounts(items);
}
private List getItemsWithCounts(final Iterable items) {
final Map snapshotCounts = getFlowSnapshotCounts();
final Map extensionBundleVersionCounts = getExtensionBundleVersionCounts();
final List itemWithCounts = new ArrayList<>();
for (final BucketItemEntity item : items) {
if (item.getType() == BucketItemEntityType.FLOW) {
final Long snapshotCount = snapshotCounts.get(item.getId());
if (snapshotCount != null) {
final FlowEntity flowEntity = (FlowEntity) item;
flowEntity.setSnapshotCount(snapshotCount);
}
} else if (item.getType() == BucketItemEntityType.BUNDLE) {
final Long versionCount = extensionBundleVersionCounts.get(item.getId());
if (versionCount != null) {
final BundleEntity bundleEntity = (BundleEntity) item;
bundleEntity.setVersionCount(versionCount);
}
}
itemWithCounts.add(item);
}
return itemWithCounts;
}
private Map getFlowSnapshotCounts() {
final String sql = "SELECT flow_id, count(*) FROM FLOW_SNAPSHOT GROUP BY flow_id";
final Map results = new HashMap<>();
jdbcTemplate.query(sql, (rs) -> {
results.put(rs.getString(1), rs.getLong(2));
});
return results;
}
private Long getFlowSnapshotCount(final String flowIdentifier) {
final String sql = "SELECT count(*) FROM FLOW_SNAPSHOT WHERE flow_id = ?";
return jdbcTemplate.queryForObject(sql, (rs, num) -> {
return rs.getLong(1);
}, flowIdentifier);
}
private Map getExtensionBundleVersionCounts() {
final String sql = "SELECT bundle_id, count(*) FROM BUNDLE_VERSION GROUP BY bundle_id";
final Map results = new HashMap<>();
jdbcTemplate.query(sql, (rs) -> {
results.put(rs.getString(1), rs.getLong(2));
});
return results;
}
private Long getExtensionBundleVersionCount(final String extensionBundleIdentifier) {
final String sql = "SELECT count(*) FROM BUNDLE_VERSION WHERE bundle_id = ?";
return jdbcTemplate.queryForObject(sql, (rs, num) -> {
return rs.getLong(1);
}, extensionBundleIdentifier);
}
//----------------- Flows ---------------------------------
@Override
public FlowEntity createFlow(final FlowEntity flow) {
final String itemSql = "INSERT INTO BUCKET_ITEM (ID, NAME, DESCRIPTION, CREATED, MODIFIED, ITEM_TYPE, BUCKET_ID) VALUES (?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(itemSql,
flow.getId(),
flow.getName(),
flow.getDescription(),
flow.getCreated(),
flow.getModified(),
flow.getType().toString(),
flow.getBucketId());
final String flowSql = "INSERT INTO FLOW (ID) VALUES (?)";
jdbcTemplate.update(flowSql, flow.getId());
return flow;
}
@Override
public FlowEntity getFlowById(final String flowIdentifier) {
final String sql = "SELECT * FROM FLOW f, BUCKET_ITEM item WHERE f.id = ? AND item.id = f.id";
try {
return jdbcTemplate.queryForObject(sql, new FlowEntityRowMapper(), flowIdentifier);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public FlowEntity getFlowByIdWithSnapshotCounts(final String flowIdentifier) {
final FlowEntity flowEntity = getFlowById(flowIdentifier);
if (flowEntity == null) {
return flowEntity;
}
final Long snapshotCount = getFlowSnapshotCount(flowIdentifier);
if (snapshotCount != null) {
flowEntity.setSnapshotCount(snapshotCount);
}
return flowEntity;
}
@Override
public List getFlowsByName(final String name) {
final String sql = "SELECT * FROM FLOW f, BUCKET_ITEM item WHERE item.name = ? AND item.id = f.id";
return jdbcTemplate.query(sql, new FlowEntityRowMapper(), name);
}
@Override
public List getFlowsByName(final String bucketIdentifier, final String name) {
final String sql = "SELECT * FROM FLOW f, BUCKET_ITEM item WHERE item.name = ? AND item.id = f.id AND item.bucket_id = ?";
return jdbcTemplate.query(sql, new FlowEntityRowMapper(), name, bucketIdentifier);
}
@Override
public List getFlowsByBucket(final String bucketIdentifier) {
final String sql = "SELECT * FROM FLOW f, BUCKET_ITEM item WHERE item.bucket_id = ? AND item.id = f.id";
final List flows = jdbcTemplate.query(sql, new FlowEntityRowMapper(), bucketIdentifier);
final Map snapshotCounts = getFlowSnapshotCounts();
for (final FlowEntity flowEntity : flows) {
final Long snapshotCount = snapshotCounts.get(flowEntity.getId());
if (snapshotCount != null) {
flowEntity.setSnapshotCount(snapshotCount);
}
}
return flows;
}
@Override
public FlowEntity updateFlow(final FlowEntity flow) {
flow.setModified(new Date());
final String sql = "UPDATE BUCKET_ITEM SET name = ?, description = ?, modified = ? WHERE id = ?";
jdbcTemplate.update(sql, flow.getName(), flow.getDescription(), flow.getModified(), flow.getId());
return flow;
}
@Override
public void deleteFlow(final FlowEntity flow) {
// NOTE: Cascading deletes will delete from child tables
final String itemDeleteSql = "DELETE FROM BUCKET_ITEM WHERE id = ?";
jdbcTemplate.update(itemDeleteSql, flow.getId());
}
//----------------- Flow Snapshots ---------------------------------
@Override
public FlowSnapshotEntity createFlowSnapshot(final FlowSnapshotEntity flowSnapshot) {
final String sql = "INSERT INTO FLOW_SNAPSHOT (FLOW_ID, VERSION, CREATED, CREATED_BY, COMMENTS) VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(sql,
flowSnapshot.getFlowId(),
flowSnapshot.getVersion(),
flowSnapshot.getCreated(),
flowSnapshot.getCreatedBy(),
flowSnapshot.getComments());
return flowSnapshot;
}
@Override
public FlowSnapshotEntity getFlowSnapshot(final String flowIdentifier, final Integer version) {
final String sql =
"SELECT " +
"fs.flow_id, " +
"fs.version, " +
"fs.created, " +
"fs.created_by, " +
"fs.comments " +
"FROM " +
"FLOW_SNAPSHOT fs, " +
"FLOW f, " +
"BUCKET_ITEM item " +
"WHERE " +
"item.id = f.id AND " +
"f.id = ? AND " +
"f.id = fs.flow_id AND " +
"fs.version = ?";
try {
return jdbcTemplate.queryForObject(sql, new FlowSnapshotEntityRowMapper(),
flowIdentifier, version);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public FlowSnapshotEntity getLatestSnapshot(final String flowIdentifier) {
final String sql = "SELECT * FROM FLOW_SNAPSHOT WHERE flow_id = ? ORDER BY version DESC LIMIT 1";
try {
return jdbcTemplate.queryForObject(sql, new FlowSnapshotEntityRowMapper(), flowIdentifier);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public List getSnapshots(final String flowIdentifier) {
final String sql =
"SELECT " +
"fs.flow_id, " +
"fs.version, " +
"fs.created, " +
"fs.created_by, " +
"fs.comments " +
"FROM " +
"FLOW_SNAPSHOT fs, " +
"FLOW f, " +
"BUCKET_ITEM item " +
"WHERE " +
"item.id = f.id AND " +
"f.id = ? AND " +
"f.id = fs.flow_id";
return jdbcTemplate.query(sql, new FlowSnapshotEntityRowMapper(), flowIdentifier);
}
@Override
public void deleteFlowSnapshot(final FlowSnapshotEntity flowSnapshot) {
final String sql = "DELETE FROM FLOW_SNAPSHOT WHERE flow_id = ? AND version = ?";
jdbcTemplate.update(sql, flowSnapshot.getFlowId(), flowSnapshot.getVersion());
}
//----------------- Extension Bundles ---------------------------------
@Override
public BundleEntity createBundle(final BundleEntity extensionBundle) {
final String itemSql =
"INSERT INTO BUCKET_ITEM (" +
"ID, " +
"NAME, " +
"DESCRIPTION, " +
"CREATED, " +
"MODIFIED, " +
"ITEM_TYPE, " +
"BUCKET_ID) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(itemSql,
extensionBundle.getId(),
extensionBundle.getName(),
extensionBundle.getDescription(),
extensionBundle.getCreated(),
extensionBundle.getModified(),
extensionBundle.getType().name(),
extensionBundle.getBucketId());
final String bundleSql =
"INSERT INTO BUNDLE (" +
"ID, " +
"BUCKET_ID, " +
"BUNDLE_TYPE, " +
"GROUP_ID, " +
"ARTIFACT_ID) " +
"VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(bundleSql,
extensionBundle.getId(),
extensionBundle.getBucketId(),
extensionBundle.getBundleType().name(),
extensionBundle.getGroupId(),
extensionBundle.getArtifactId());
return extensionBundle;
}
private static final String BASE_BUNDLE_SQL =
"SELECT " +
"item.id as ID," +
"item.name as NAME, " +
"item.description as DESCRIPTION, " +
"item.created as CREATED, " +
"item.modified as MODIFIED, " +
"eb.bundle_type as BUNDLE_TYPE, " +
"eb.group_id as GROUP_ID, " +
"eb.artifact_id as ARTIFACT_ID, " +
"b.id as BUCKET_ID, " +
"b.name as BUCKET_NAME " +
"FROM " +
"BUNDLE eb, " +
"BUCKET_ITEM item," +
"BUCKET b " +
"WHERE " +
"eb.id = item.id AND " +
"item.bucket_id = b.id";
@Override
public BundleEntity getBundle(final String extensionBundleId) {
final StringBuilder sqlBuilder = new StringBuilder(BASE_BUNDLE_SQL).append(" AND eb.id = ?");
try {
final BundleEntity entity = jdbcTemplate.queryForObject(sqlBuilder.toString(), new BundleEntityRowMapper(), extensionBundleId);
final Long versionCount = getExtensionBundleVersionCount(extensionBundleId);
if (versionCount != null) {
entity.setVersionCount(versionCount);
}
return entity;
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public BundleEntity getBundle(final String bucketId, final String groupId, final String artifactId) {
final StringBuilder sqlBuilder = new StringBuilder(BASE_BUNDLE_SQL)
.append(" AND eb.bucket_id = ? ")
.append("AND eb.group_id = ? ")
.append("AND eb.artifact_id = ? ");
try {
final BundleEntity entity = jdbcTemplate.queryForObject(sqlBuilder.toString(), new BundleEntityRowMapper(), bucketId, groupId, artifactId);
final Long versionCount = getExtensionBundleVersionCount(entity.getId());
if (versionCount != null) {
entity.setVersionCount(versionCount);
}
return entity;
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public List getBundles(final Set bucketIds, final BundleFilterParams filterParams) {
if (bucketIds == null || bucketIds.isEmpty()) {
return Collections.emptyList();
}
final List