Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SQL implementation of TargetNodeDirectory #241

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
maven/mavencentral/com.apicatalog/carbon-did/0.3.0, Apache-2.0, approved, clearlydefined

Check warning on line 1 in DEPENDENCIES

View workflow job for this annotation

GitHub Actions / check / Dash-Verify-Licenses

Restricted Dependencies found

Some dependencies are marked 'restricted' - please review them
maven/mavencentral/com.apicatalog/copper-multibase/0.5.0, Apache-2.0, approved, #14501
maven/mavencentral/com.apicatalog/copper-multicodec/0.1.1, Apache-2.0, approved, #14500
maven/mavencentral/com.apicatalog/iron-ed25519-cryptosuite-2020/0.14.0, Apache-2.0, approved, #14503
Expand Down Expand Up @@ -218,6 +218,7 @@
maven/mavencentral/org.eclipse.edc/junit-base/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/junit/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/jws2020-lib/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/jwt-signer-spi/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/jwt-spi/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/jwt-verifiable-credentials/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/keys-lib/0.8.2-SNAPSHOT, Apache-2.0, approved, technology.edc
Expand Down Expand Up @@ -314,12 +315,18 @@
maven/mavencentral/org.jetbrains/annotations/24.1.0, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.junit-pioneer/junit-pioneer/2.2.0, EPL-2.0, approved, #11857
maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.10.3, EPL-2.0, approved, #9714
maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.11.0, EPL-2.0, approved, #15935
maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.10.3, EPL-2.0, approved, #9711
maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.11.0, EPL-2.0, approved, #15939
maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.10.3, EPL-2.0, approved, #15250
maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.11.0, EPL-2.0, approved, #15940
maven/mavencentral/org.junit.platform/junit-platform-commons/1.10.3, EPL-2.0, approved, #9715
maven/mavencentral/org.junit.platform/junit-platform-commons/1.11.0, EPL-2.0, approved, #15936
maven/mavencentral/org.junit.platform/junit-platform-engine/1.10.3, EPL-2.0, approved, #9709
maven/mavencentral/org.junit.platform/junit-platform-launcher/1.10.3, EPL-2.0, approved, #15216
maven/mavencentral/org.junit.platform/junit-platform-engine/1.11.0, EPL-2.0, approved, #15932
maven/mavencentral/org.junit.platform/junit-platform-launcher/1.11.0, EPL-2.0, approved, #15934
maven/mavencentral/org.junit/junit-bom/5.10.3, EPL-2.0, approved, #9844
maven/mavencentral/org.junit/junit-bom/5.11.0, , restricted, clearlydefined
maven/mavencentral/org.junit/junit-bom/5.9.2, EPL-2.0, approved, #4711
maven/mavencentral/org.jvnet.mimepull/mimepull/1.9.15, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, CQ21484
maven/mavencentral/org.mockito/mockito-core/5.12.0, MIT AND (Apache-2.0 AND MIT) AND Apache-2.0, approved, #14678
Expand Down
1 change: 1 addition & 0 deletions core/federated-catalog-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ dependencies {
testImplementation(libs.awaitility)

testImplementation(testFixtures(project(":spi:federated-catalog-spi")))
testImplementation(testFixtures(project(":spi:crawler-spi")))
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@
import org.eclipse.edc.crawler.spi.TargetNodeDirectory;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class InMemoryNodeDirectory implements TargetNodeDirectory {
private final List<TargetNode> cache = new CopyOnWriteArrayList<>();
private final Map<String, TargetNode> cache = new ConcurrentHashMap<>();

@Override
public List<TargetNode> getAll() {
return List.copyOf(cache); //never return the internal copy
return List.copyOf(cache.values()); //never return the internal copy
}

@Override
public void insert(TargetNode node) {
cache.add(node);
cache.put(node.id(), node);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Amadeus IT Group
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Amadeus IT Group - initial API and implementation
*
*/

package org.eclipse.edc.catalog.directory;

import org.eclipse.edc.catalog.spi.testfixtures.TargetNodeDirectoryTestBase;
import org.eclipse.edc.crawler.spi.TargetNodeDirectory;

class InMemoryNodeDirectoryTest extends TargetNodeDirectoryTestBase {

private final InMemoryNodeDirectory store = new InMemoryNodeDirectory();

@Override
protected TargetNodeDirectory getStore() {
return store;
}

}
29 changes: 29 additions & 0 deletions extensions/store/sql/target-node-directory-sql/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Amadeus IT Group
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Amadeus IT Group - initial API and implementation
*
*/

plugins {
`java-library`
}

dependencies {
api(project(":spi:crawler-spi"))
implementation(libs.edc.sql.core) // for the SqlStatements
implementation(libs.edc.sql.bootstrapper)
implementation(libs.edc.spi.transaction.datasource)
implementation(libs.edc.lib.util)

testImplementation(libs.edc.junit)
testImplementation(testFixtures(libs.edc.sql.core))
testImplementation(testFixtures(project(":spi:crawler-spi")))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 Amadeus IT Group
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Amadeus IT Group - initial API and implementation
*
*/

package org.eclipse.edc.catalog.store.sql;

import org.eclipse.edc.catalog.store.sql.schema.postgres.TargetNodeMapping;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.sql.translation.PostgresqlOperatorTranslator;
import org.eclipse.edc.sql.translation.SqlQueryStatement;

import static java.lang.String.format;

public abstract class BaseSqlDialectStatements implements TargetNodeStatements {

@Override
public String getFindByIdTemplate() {
return format("SELECT * FROM %s WHERE %s = ?", getTargetNodeDirectoryTable(), getIdColumn());
}

@Override
public String getUpdateTemplate() {
return executeStatement()
.column(getNameColumn())
.column(getTargetUrlColumn())
.jsonColumn(getSupportedProtocolsColumn())
.update(getTargetNodeDirectoryTable(), getIdColumn());
}

@Override
public String getInsertTemplate() {
return executeStatement()
.column(getIdColumn())
.column(getNameColumn())
.column(getTargetUrlColumn())
.jsonColumn(getSupportedProtocolsColumn())
.insertInto(getTargetNodeDirectoryTable());
}

@Override
public SqlQueryStatement createQuery(QuerySpec querySpec) {
var select = getSelectStatement();
return new SqlQueryStatement(select, querySpec, new TargetNodeMapping(this), new PostgresqlOperatorTranslator());
}

@Override
public String getSelectStatement() {
return format("SELECT * FROM %s", getTargetNodeDirectoryTable());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2024 Amadeus IT Group
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Amadeus IT Group - initial API and implementation
*
*/

package org.eclipse.edc.catalog.store.sql;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.edc.crawler.spi.TargetNode;
import org.eclipse.edc.crawler.spi.TargetNodeDirectory;
import org.eclipse.edc.spi.persistence.EdcPersistenceException;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.sql.QueryExecutor;
import org.eclipse.edc.sql.store.AbstractSqlStore;
import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry;
import org.eclipse.edc.transaction.spi.TransactionContext;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class SqlTargetNodeDirectory extends AbstractSqlStore implements TargetNodeDirectory {

private final TargetNodeStatements statements;

public SqlTargetNodeDirectory(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext,
ObjectMapper objectMapper, QueryExecutor queryExecutor, TargetNodeStatements statements) {
super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor);
this.statements = statements;
}

@Override
public List<TargetNode> getAll() {
return transactionContext.execute(() -> {
try (var connection = getConnection()) {
var query = statements.createQuery(QuerySpec.max());
return queryExecutor.query(connection, true, this::mapResultSet, query.getQueryAsString(), query.getParameters()).toList();
} catch (SQLException e) {
throw new EdcPersistenceException(e);
}
});
}

@Override
public void insert(TargetNode node) {
transactionContext.execute(() -> {
try (var connection = getConnection()) {
var id = node.id();

if (findByIdInternal(connection, id) == null) {
insertInternal(connection, id, node);
} else {
updateInternal(connection, id, node);
}

} catch (SQLException e) {
throw new EdcPersistenceException(e);
}
});
}

private TargetNode findByIdInternal(Connection connection, String id) {
var stmt = statements.getFindByIdTemplate();
return queryExecutor.single(connection, false, this::mapResultSet, stmt, id);
}

private void insertInternal(Connection connection, String id, TargetNode targetNode) {
var stmt = statements.getInsertTemplate();
queryExecutor.execute(connection,
stmt,
id,
targetNode.name(),
targetNode.targetUrl(),
toJson(targetNode.supportedProtocols())
);
}

private void updateInternal(Connection connection, String id, TargetNode targetNode) {
var stmt = statements.getUpdateTemplate();
queryExecutor.execute(connection,
stmt,
targetNode.name(),
targetNode.targetUrl(),
toJson(targetNode.supportedProtocols()),
id
);
}

private TargetNode mapResultSet(ResultSet resultSet) throws Exception {
return new TargetNode(
resultSet.getString(statements.getNameColumn()),
resultSet.getString(statements.getIdColumn()),
resultSet.getString(statements.getTargetUrlColumn()),
fromJson(resultSet.getString(statements.getSupportedProtocolsColumn()), new TypeReference<>() {
})
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2024 Amadeus IT Group
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Amadeus IT Group - initial API and implementation
*
*/

package org.eclipse.edc.catalog.store.sql;

import org.eclipse.edc.catalog.store.sql.schema.postgres.PostgresDialectStatements;
import org.eclipse.edc.crawler.spi.TargetNode;
import org.eclipse.edc.crawler.spi.TargetNodeDirectory;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provides;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.types.TypeManager;
import org.eclipse.edc.sql.QueryExecutor;
import org.eclipse.edc.sql.bootstrapper.SqlSchemaBootstrapper;
import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry;
import org.eclipse.edc.transaction.spi.TransactionContext;

@Provides(TargetNodeDirectory.class)
@Extension(value = "SQL target node directory")
public class SqlTargetNodeDirectoryExtension implements ServiceExtension {

@Setting(value = "The datasource to be used", defaultValue = DataSourceRegistry.DEFAULT_DATASOURCE)
public static final String DATASOURCE_NAME = "edc.sql.store.targetnodedirectory.datasource";

@Inject
private DataSourceRegistry dataSourceRegistry;
@Inject
private TransactionContext trxContext;
@Inject(required = false)
private TargetNodeStatements statements;
@Inject
private TypeManager typeManager;

@Inject
private QueryExecutor queryExecutor;

@Inject
private SqlSchemaBootstrapper sqlSchemaBootstrapper;

@Override
public void initialize(ServiceExtensionContext context) {
typeManager.registerTypes(TargetNode.class);
var dataSourceName = context.getSetting(DATASOURCE_NAME, DataSourceRegistry.DEFAULT_DATASOURCE);
var targetNodeDirectory = new SqlTargetNodeDirectory(dataSourceRegistry, dataSourceName, trxContext,
typeManager.getMapper(), queryExecutor, getStatementImpl());
context.registerService(TargetNodeDirectory.class, targetNodeDirectory);
sqlSchemaBootstrapper.addStatementFromResource(dataSourceName, "target-node-directory-schema.sql");
}

/**
* returns an externally-provided sql statement dialect, or postgres as a default
*/
private TargetNodeStatements getStatementImpl() {
return statements != null ? statements : new PostgresDialectStatements();
}

}
Loading
Loading