blob: 2ab945b84ba001ea66540124e8466cf9cdc2086a [file] [log] [blame]
/*
* 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 org.apache.commons.lang3.StringUtils;
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.extension.component.ExtensionFilterParams;
import org.apache.nifi.registry.extension.component.manifest.ExtensionType;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
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;
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;
@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) VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(sql,
b.getId(),
b.getName(),
b.getDescription(),
b.getCreated(),
b.isAllowExtensionBundleRedeploy() ? 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<BucketEntity> getBucketsByName(final String name) {
final String sql = "SELECT * FROM bucket WHERE name = ? ORDER BY name ASC";
return jdbcTemplate.query(sql, new Object[] {name} , new BucketEntityRowMapper());
}
@Override
public BucketEntity updateBucket(final BucketEntity bucket) {
final String sql = "UPDATE bucket SET name = ?, description = ?, allow_extension_bundle_redeploy = ? WHERE id = ?";
jdbcTemplate.update(sql, bucket.getName(), bucket.getDescription(), bucket.isAllowExtensionBundleRedeploy() ? 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<BucketEntity> getBuckets(final Set<String> 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(), bucketIds.toArray(), new BucketEntityRowMapper());
}
@Override
public List<BucketEntity> 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<BucketItemEntity> getBucketItems(final String bucketIdentifier) {
final String sql = BASE_BUCKET_ITEMS_SQL + " WHERE item.bucket_id = ?";
final List<BucketItemEntity> items = jdbcTemplate.query(sql, new Object[] { bucketIdentifier }, new BucketItemEntityRowMapper());
return getItemsWithCounts(items);
}
@Override
public List<BucketItemEntity> getBucketItems(final Set<String> 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<BucketItemEntity> items = jdbcTemplate.query(sqlBuilder.toString(), bucketIds.toArray(), new BucketItemEntityRowMapper());
return getItemsWithCounts(items);
}
private List<BucketItemEntity> getItemsWithCounts(final Iterable<BucketItemEntity> items) {
final Map<String,Long> snapshotCounts = getFlowSnapshotCounts();
final Map<String,Long> extensionBundleVersionCounts = getExtensionBundleVersionCounts();
final List<BucketItemEntity> 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.EXTENSION_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<String,Long> getFlowSnapshotCounts() {
final String sql = "SELECT flow_id, count(*) FROM flow_snapshot GROUP BY flow_id";
final Map<String,Long> 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, new Object[] {flowIdentifier}, (rs, num) -> {
return rs.getLong(1);
});
}
private Map<String,Long> getExtensionBundleVersionCounts() {
final String sql = "SELECT bundle_id, count(*) FROM bundle_version GROUP BY bundle_id";
final Map<String,Long> 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, new Object[] {extensionBundleIdentifier}, (rs, num) -> {
return rs.getLong(1);
});
}
//----------------- 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<FlowEntity> 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 Object[] {name}, new FlowEntityRowMapper());
}
@Override
public List<FlowEntity> 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 Object[] {name, bucketIdentifier}, new FlowEntityRowMapper());
}
@Override
public List<FlowEntity> 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<FlowEntity> flows = jdbcTemplate.query(sql, new Object[] {bucketIdentifier}, new FlowEntityRowMapper());
final Map<String,Long> 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<FlowSnapshotEntity> 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";
final Object[] args = new Object[] { flowIdentifier };
return jdbcTemplate.query(sql, args, new FlowSnapshotEntityRowMapper());
}
@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<BundleEntity> getBundles(final Set<String> bucketIds, final BundleFilterParams filterParams) {
if (bucketIds == null || bucketIds.isEmpty()) {
return Collections.emptyList();
}
final List<Object> args = new ArrayList<>();
final StringBuilder sqlBuilder = new StringBuilder(
"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 " +
"bundle eb, " +
"bucket_item item, " +
"bucket b " +
"WHERE " +
"item.id = eb.id AND " +
"b.id = item.bucket_id");
if (filterParams != null) {
final String bucketName = filterParams.getBucketName();
if (!StringUtils.isBlank(bucketName)) {
sqlBuilder.append(" AND b.name LIKE ? ");
args.add(bucketName);
}
final String groupId = filterParams.getGroupId();
if (!StringUtils.isBlank(groupId)) {
sqlBuilder.append(" AND eb.group_id LIKE ? ");
args.add(groupId);
}
final String artifactId = filterParams.getArtifactId();
if (!StringUtils.isBlank(artifactId)) {
sqlBuilder.append(" AND eb.artifact_id LIKE ? ");
args.add(artifactId);
}
}
sqlBuilder.append(" AND ");
addIdentifiersInClause(sqlBuilder, "item.bucket_id", bucketIds);
sqlBuilder.append("ORDER BY eb.group_id ASC, eb.artifact_id ASC");
args.addAll(bucketIds);
final List<BundleEntity> bundleEntities = jdbcTemplate.query(sqlBuilder.toString(), args.toArray(), new BundleEntityRowMapper());
return populateVersionCounts(bundleEntities);
}
@Override
public List<BundleEntity> getBundlesByBucket(final String bucketId) {
final StringBuilder sqlBuilder = new StringBuilder(BASE_BUNDLE_SQL)
.append(" AND b.id = ?")
.append(" ORDER BY eb.group_id ASC, eb.artifact_id ASC");
final List<BundleEntity> bundles = jdbcTemplate.query(sqlBuilder.toString(), new Object[]{bucketId}, new BundleEntityRowMapper());
return populateVersionCounts(bundles);
}
@Override
public List<BundleEntity> getBundlesByBucketAndGroup(String bucketId, String groupId) {
final StringBuilder sqlBuilder = new StringBuilder(BASE_BUNDLE_SQL)
.append(" AND b.id = ?")
.append(" AND eb.group_id = ?")
.append(" ORDER BY eb.group_id ASC, eb.artifact_id ASC");
final List<BundleEntity> bundles = jdbcTemplate.query(sqlBuilder.toString(), new Object[]{bucketId, groupId}, new BundleEntityRowMapper());
return populateVersionCounts(bundles);
}
private List<BundleEntity> populateVersionCounts(final List<BundleEntity> bundles) {
if (!bundles.isEmpty()) {
final Map<String, Long> versionCounts = getExtensionBundleVersionCounts();
for (final BundleEntity entity : bundles) {
final Long versionCount = versionCounts.get(entity.getId());
if (versionCount != null) {
entity.setVersionCount(versionCount);
}
}
}
return bundles;
}
@Override
public void deleteBundle(final BundleEntity extensionBundle) {
deleteBundle(extensionBundle.getId());
}
@Override
public void deleteBundle(final String extensionBundleId) {
// NOTE: All of the foreign key constraints for extension related tables are set to cascade on delete
final String itemDeleteSql = "DELETE FROM bucket_item WHERE id = ?";
jdbcTemplate.update(itemDeleteSql, extensionBundleId);
}
//----------------- Extension Bundle Versions ---------------------------------
@Override
public BundleVersionEntity createBundleVersion(final BundleVersionEntity extensionBundleVersion) {
final String sql =
"INSERT INTO bundle_version (" +
"ID, " +
"BUNDLE_ID, " +
"VERSION, " +
"CREATED, " +
"CREATED_BY, " +
"DESCRIPTION, " +
"SHA_256_HEX, " +
"SHA_256_SUPPLIED," +
"CONTENT_SIZE, " +
"SYSTEM_API_VERSION, " +
"BUILD_TOOL, " +
"BUILD_FLAGS, " +
"BUILD_BRANCH, " +
"BUILD_TAG, " +
"BUILD_REVISION, " +
"BUILT, " +
"BUILT_BY" +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sql,
extensionBundleVersion.getId(),
extensionBundleVersion.getBundleId(),
extensionBundleVersion.getVersion(),
extensionBundleVersion.getCreated(),
extensionBundleVersion.getCreatedBy(),
extensionBundleVersion.getDescription(),
extensionBundleVersion.getSha256Hex(),
extensionBundleVersion.getSha256Supplied() ? 1 : 0,
extensionBundleVersion.getContentSize(),
extensionBundleVersion.getSystemApiVersion(),
extensionBundleVersion.getBuildTool(),
extensionBundleVersion.getBuildFlags(),
extensionBundleVersion.getBuildBranch(),
extensionBundleVersion.getBuildTag(),
extensionBundleVersion.getBuildRevision(),
extensionBundleVersion.getBuilt(),
extensionBundleVersion.getBuiltBy());
return extensionBundleVersion;
}
private static final String BASE_EXTENSION_BUNDLE_VERSION_SQL =
"SELECT " +
"ebv.id AS ID," +
"ebv.bundle_id AS BUNDLE_ID, " +
"ebv.version AS VERSION, " +
"ebv.created AS CREATED, " +
"ebv.created_by AS CREATED_BY, " +
"ebv.description AS DESCRIPTION, " +
"ebv.sha_256_hex AS SHA_256_HEX, " +
"ebv.sha_256_supplied AS SHA_256_SUPPLIED ," +
"ebv.content_size AS CONTENT_SIZE, " +
"ebv.system_api_version AS SYSTEM_API_VERSION, " +
"ebv.build_tool AS BUILD_TOOL, " +
"ebv.build_flags AS BUILD_FLAGS, " +
"ebv.build_branch AS BUILD_BRANCH, " +
"ebv.build_tag AS BUILD_TAG, " +
"ebv.build_revision AS BUILD_REVISION, " +
"ebv.built AS BUILT, " +
"ebv.built_by AS BUILT_BY, " +
"eb.bucket_id AS BUCKET_ID " +
"FROM bundle eb, bundle_version ebv " +
"WHERE eb.id = ebv.bundle_id ";
@Override
public BundleVersionEntity getBundleVersion(final String extensionBundleId, final String version) {
final String sql = BASE_EXTENSION_BUNDLE_VERSION_SQL +
" AND ebv.bundle_id = ? AND ebv.version = ?";
try {
return jdbcTemplate.queryForObject(sql, new BundleVersionEntityRowMapper(), extensionBundleId, version);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public BundleVersionEntity getBundleVersion(final String bucketId, final String groupId, final String artifactId, final String version) {
final String sql = BASE_EXTENSION_BUNDLE_VERSION_SQL +
"AND eb.bucket_id = ? " +
"AND eb.group_id = ? " +
"AND eb.artifact_id = ? " +
"AND ebv.version = ?";
try {
return jdbcTemplate.queryForObject(sql, new BundleVersionEntityRowMapper(), bucketId, groupId, artifactId, version);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public List<BundleVersionEntity> getBundleVersions(final Set<String> bucketIdentifiers, final BundleVersionFilterParams filterParams) {
if (bucketIdentifiers == null || bucketIdentifiers.isEmpty()) {
return Collections.emptyList();
}
final List<Object> args = new ArrayList<>();
final StringBuilder sqlBuilder = new StringBuilder(BASE_EXTENSION_BUNDLE_VERSION_SQL);
if (filterParams != null) {
final String groupId = filterParams.getGroupId();
if (!StringUtils.isBlank(groupId)) {
sqlBuilder.append(" AND eb.group_id LIKE ? ");
args.add(groupId);
}
final String artifactId = filterParams.getArtifactId();
if (!StringUtils.isBlank(artifactId)) {
sqlBuilder.append(" AND eb.artifact_id LIKE ? ");
args.add(artifactId);
}
final String version = filterParams.getVersion();
if (!StringUtils.isBlank(version)) {
sqlBuilder.append(" AND ebv.version LIKE ? ");
args.add(version);
}
}
sqlBuilder.append(" AND ");
addIdentifiersInClause(sqlBuilder, "eb.bucket_id", bucketIdentifiers);
args.addAll(bucketIdentifiers);
final List<BundleVersionEntity> bundleVersionEntities = jdbcTemplate.query(
sqlBuilder.toString(), args.toArray(), new BundleVersionEntityRowMapper());
return bundleVersionEntities;
}
private void addIdentifiersInClause(StringBuilder sqlBuilder, String idFieldName, Set<String> identifiers) {
sqlBuilder.append(idFieldName).append(" IN (");
for (int i = 0; i < identifiers.size(); i++) {
if (i > 0) {
sqlBuilder.append(", ");
}
sqlBuilder.append("?");
}
sqlBuilder.append(") ");
}
@Override
public List<BundleVersionEntity> getBundleVersions(final String extensionBundleId) {
final String sql = BASE_EXTENSION_BUNDLE_VERSION_SQL + " AND ebv.bundle_id = ?";
return jdbcTemplate.query(sql, new Object[]{extensionBundleId}, new BundleVersionEntityRowMapper());
}
@Override
public List<BundleVersionEntity> getBundleVersions(final String bucketId, final String groupId, final String artifactId) {
final String sql = BASE_EXTENSION_BUNDLE_VERSION_SQL +
"AND eb.bucket_id = ? " +
"AND eb.group_id = ? " +
"AND eb.artifact_id = ? ";
final Object[] args = {bucketId, groupId, artifactId};
return jdbcTemplate.query(sql, args, new BundleVersionEntityRowMapper());
}
@Override
public List<BundleVersionEntity> getBundleVersionsGlobal(final String groupId, final String artifactId, final String version) {
final String sql = BASE_EXTENSION_BUNDLE_VERSION_SQL +
"AND eb.group_id = ? " +
"AND eb.artifact_id = ? " +
"AND ebv.version = ?";
final Object[] args = {groupId, artifactId, version};
return jdbcTemplate.query(sql, args, new BundleVersionEntityRowMapper());
}
@Override
public void deleteBundleVersion(final BundleVersionEntity extensionBundleVersion) {
deleteBundleVersion(extensionBundleVersion.getId());
}
@Override
public void deleteBundleVersion(final String extensionBundleVersionId) {
// NOTE: All of the foreign key constraints for extension related tables are set to cascade on delete
final String sql = "DELETE FROM bundle_version WHERE id = ?";
jdbcTemplate.update(sql, extensionBundleVersionId);
}
//------------ Extension Bundle Version Dependencies ------------
@Override
public BundleVersionDependencyEntity createDependency(final BundleVersionDependencyEntity dependencyEntity) {
final String dependencySql =
"INSERT INTO bundle_version_dependency (" +
"ID, " +
"BUNDLE_VERSION_ID, " +
"GROUP_ID, " +
"ARTIFACT_ID, " +
"VERSION " +
") VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(dependencySql,
dependencyEntity.getId(),
dependencyEntity.getExtensionBundleVersionId(),
dependencyEntity.getGroupId(),
dependencyEntity.getArtifactId(),
dependencyEntity.getVersion());
return dependencyEntity;
}
@Override
public List<BundleVersionDependencyEntity> getDependenciesForBundleVersion(final String extensionBundleVersionId) {
final String sql = "SELECT * FROM bundle_version_dependency WHERE bundle_version_id = ?";
final Object[] args = {extensionBundleVersionId};
return jdbcTemplate.query(sql, args, new BundleVersionDependencyEntityRowMapper());
}
//----------------- Extensions ---------------------------------
private static String BASE_EXTENSION_SQL =
"SELECT " +
"e.id AS ID, " +
"e.bundle_version_id AS BUNDLE_VERSION_ID, " +
"e.name AS NAME, " +
"e.display_name AS DISPLAY_NAME, " +
"e.type AS TYPE, " +
"e.content AS CONTENT," +
"e.has_additional_details AS HAS_ADDITIONAL_DETAILS, " +
"eb.id AS BUNDLE_ID, " +
"eb.group_id AS GROUP_ID, " +
"eb.artifact_id AS ARTIFACT_ID, " +
"eb.bundle_type AS BUNDLE_TYPE, " +
"ebv.version AS VERSION, " +
"ebv.system_api_version AS SYSTEM_API_VERSION, " +
"b.id AS BUCKET_ID, " +
"b.name as BUCKET_NAME " +
"FROM " +
"extension e, " +
"bundle_version ebv, " +
"bundle eb," +
"bucket b " +
"WHERE " +
"e.bundle_version_id = ebv.id AND " +
"ebv.bundle_id = eb.id AND " +
"eb.bucket_id = b.id ";
@Override
public ExtensionEntity createExtension(final ExtensionEntity extension) {
final String insertExtensionSql =
"INSERT INTO extension (" +
"ID, " +
"BUNDLE_VERSION_ID, " +
"NAME, " +
"DISPLAY_NAME, " +
"TYPE, " +
"CONTENT, " +
"ADDITIONAL_DETAILS, " +
"HAS_ADDITIONAL_DETAILS " +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(insertExtensionSql,
extension.getId(),
extension.getBundleVersionId(),
extension.getName(),
extension.getDisplayName(),
extension.getExtensionType().name(),
extension.getContent(),
extension.getAdditionalDetails(),
extension.getAdditionalDetails() != null ? 1 : 0
);
// insert tags...
final String insertTagSql = "INSERT INTO extension_tag (EXTENSION_ID, TAG) VALUES (?, ?);";
final Set<String> tags = extension.getTags();
if (tags != null) {
for (final String tag : tags) {
if (tag != null) {
final String normalizedTag = tag.trim().toLowerCase();
if (!normalizedTag.isEmpty()) {
jdbcTemplate.update(insertTagSql, extension.getId(), normalizedTag);
}
}
}
}
// insert provided service APIs...
final Set<ExtensionProvidedServiceApiEntity> providedServiceApis = extension.getProvidedServiceApis();
if (providedServiceApis != null) {
providedServiceApis.forEach(p -> createProvidedServiceApi(p));
}
// insert restrictions...
final Set<ExtensionRestrictionEntity> restrictions = extension.getRestrictions();
if (restrictions != null) {
restrictions.forEach(r -> createRestriction(r));
}
return extension;
}
@Override
public ExtensionEntity getExtensionById(final String id) {
final String selectSql = BASE_EXTENSION_SQL + " AND e.id = ?";
try {
return jdbcTemplate.queryForObject(selectSql, new ExtensionEntityRowMapper(), id);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public ExtensionEntity getExtensionByName(final String bundleVersionId, final String name) {
final String selectSql = BASE_EXTENSION_SQL + " AND e.bundle_version_id = ? AND e.name = ?";
try {
return jdbcTemplate.queryForObject(selectSql, new ExtensionEntityRowMapper(), bundleVersionId, name);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public ExtensionAdditionalDetailsEntity getExtensionAdditionalDetails(final String bundleVersionId, final String name) {
final String selectSql = "SELECT id, additional_details FROM extension WHERE bundle_version_id = ? AND name = ?";
try {
final Object[] args = {bundleVersionId, name};
return jdbcTemplate.queryForObject(selectSql, args, (rs, i) -> {
final ExtensionAdditionalDetailsEntity entity = new ExtensionAdditionalDetailsEntity();
entity.setExtensionId(rs.getString("ID"));
entity.setAdditionalDetails(Optional.ofNullable(rs.getString("ADDITIONAL_DETAILS")));
return entity;
});
} catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public List<ExtensionEntity> getExtensions(final Set<String> bucketIdentifiers, final ExtensionFilterParams filterParams) {
if (bucketIdentifiers == null || bucketIdentifiers.isEmpty()) {
return Collections.emptyList();
}
final List<Object> args = new ArrayList<>();
final StringBuilder sqlBuilder = new StringBuilder(BASE_EXTENSION_SQL);
sqlBuilder.append(" AND ");
addIdentifiersInClause(sqlBuilder, "eb.bucket_id", bucketIdentifiers);
args.addAll(bucketIdentifiers);
if (filterParams != null) {
final BundleType bundleType = filterParams.getBundleType();
if (bundleType != null) {
sqlBuilder.append(" AND eb.bundle_type = ?");
args.add(bundleType.name());
}
final ExtensionType extensionType = filterParams.getExtensionType();
if (extensionType != null) {
sqlBuilder.append(" AND e.type = ?");
args.add(extensionType.name());
}
final Collection<String> tags = filterParams.getTags();
if (tags != null && !tags.isEmpty()) {
sqlBuilder.append(" AND e.id IN (")
.append(" SELECT et.extension_id FROM extension_tag et WHERE ");
boolean first = true;
for (final String tag : tags) {
if (!first) {
sqlBuilder.append(" OR ");
}
sqlBuilder.append(" et.tag = ? ");
args.add(tag.trim().toLowerCase());
first = false;
}
sqlBuilder.append(")");
}
}
sqlBuilder.append(" ORDER BY e.name ASC");
return jdbcTemplate.query(sqlBuilder.toString(), args.toArray(), new ExtensionEntityRowMapper());
}
@Override
public List<ExtensionEntity> getExtensionsByProvidedServiceApi(final Set<String> bucketIdentifiers, final ProvidedServiceAPI providedServiceAPI) {
if (bucketIdentifiers == null || bucketIdentifiers.isEmpty()) {
return Collections.emptyList();
}
final List<Object> args = new ArrayList<>();
final StringBuilder sqlBuilder = new StringBuilder(BASE_EXTENSION_SQL);
sqlBuilder.append(" AND ");
addIdentifiersInClause(sqlBuilder, "eb.bucket_id", bucketIdentifiers);
args.addAll(bucketIdentifiers);
sqlBuilder.append(" AND e.id IN (")
.append(" SELECT ep.extension_id FROM extension_provided_service_api ep")
.append(" WHERE ep.class_name = ? ")
.append(" AND ep.group_id = ? ")
.append(" AND ep.artifact_id = ? ")
.append(" AND ep.version = ?")
.append(")")
.toString();
args.add(providedServiceAPI.getClassName());
args.add(providedServiceAPI.getGroupId());
args.add(providedServiceAPI.getArtifactId());
args.add(providedServiceAPI.getVersion());
return jdbcTemplate.query(sqlBuilder.toString(), args.toArray(), new ExtensionEntityRowMapper());
}
@Override
public List<ExtensionEntity> getExtensionsByBundleVersionId(final String bundleVersionId) {
final String selectSql = BASE_EXTENSION_SQL + " AND e.bundle_version_id = ?";
final Object[] args = { bundleVersionId };
return jdbcTemplate.query(selectSql, args, new ExtensionEntityRowMapper());
}
@Override
public List<TagCountEntity> getAllExtensionTags() {
final String selectSql =
"SELECT tag as TAG, count(*) as COUNT " +
"FROM extension_tag " +
"GROUP BY tag " +
"ORDER BY tag ASC";
return jdbcTemplate.query(selectSql, new TagCountEntityMapper());
}
@Override
public void deleteExtension(final ExtensionEntity extension) {
// NOTE: All of the foreign key constraints for extension related tables are set to cascade on delete
final String deleteSql = "DELETE FROM extension WHERE id = ?";
jdbcTemplate.update(deleteSql, extension.getId());
}
//----------------- Extension Provided Service APIs --------------------
private ExtensionProvidedServiceApiEntity createProvidedServiceApi(final ExtensionProvidedServiceApiEntity providedServiceApi) {
final String sql =
"INSERT INTO extension_provided_service_api (" +
"ID, " +
"EXTENSION_ID, " +
"CLASS_NAME, " +
"GROUP_ID, " +
"ARTIFACT_ID, " +
"VERSION) " +
"VALUES (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sql,
providedServiceApi.getId(),
providedServiceApi.getExtensionId(),
providedServiceApi.getClassName(),
providedServiceApi.getGroupId(),
providedServiceApi.getArtifactId(),
providedServiceApi.getVersion()
);
return providedServiceApi;
}
//----------------- Extension Restrictions --------------------
private ExtensionRestrictionEntity createRestriction(final ExtensionRestrictionEntity restriction) {
final String sql =
"INSERT INTO extension_restriction(" +
"ID, " +
"EXTENSION_ID, " +
"REQUIRED_PERMISSION, " +
"EXPLANATION) " +
"VALUES (?, ?, ?, ?)";
jdbcTemplate.update(sql,
restriction.getId(),
restriction.getExtensionId(),
restriction.getRequiredPermission(),
restriction.getExplanation());
return restriction;
}
//----------------- Fields ---------------------------------
@Override
public Set<String> getBucketFields() {
final Set<String> fields = new LinkedHashSet<>();
fields.add("ID");
fields.add("NAME");
fields.add("DESCRIPTION");
fields.add("CREATED");
return fields;
}
@Override
public Set<String> getBucketItemFields() {
final Set<String> fields = new LinkedHashSet<>();
fields.add("ID");
fields.add("NAME");
fields.add("DESCRIPTION");
fields.add("CREATED");
fields.add("MODIFIED");
fields.add("ITEM_TYPE");
fields.add("BUCKET_ID");
return fields;
}
@Override
public Set<String> getFlowFields() {
final Set<String> fields = new LinkedHashSet<>();
fields.add("ID");
fields.add("NAME");
fields.add("DESCRIPTION");
fields.add("CREATED");
fields.add("MODIFIED");
fields.add("ITEM_TYPE");
fields.add("BUCKET_ID");
return fields;
}
}