Skip to content

Commit

Permalink
feat: federated catalog node resolver sample (#346)
Browse files Browse the repository at this point in the history
* Add federated catalog node resolver sample

* Updates participants.json file path in README.md

* Add system-tests for federated catalog sample fc-03-static-node-directory

* Code cleanup

* Update code for checkstyle

* Update crawler execution period time for system tests

* Update timeout duration for Policy01BasicTest

* Remove links to FederatedCatalog repository from README file, inject typeManager in Extension file

* Move participants.json file to resource directory, update CatalogNodeDirectoryExtension.java

* Update README for changed participants.json file path

* Update FederatedCatalog repository link in federated-catalog/fc-00-basic/README.md
  • Loading branch information
farhin23 authored Dec 18, 2024
1 parent 43513eb commit 042b0e0
Show file tree
Hide file tree
Showing 19 changed files with 485 additions and 24 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ custom code for their enforcement.

All policy samples are located in the `policy` directory.

### [Federated Catalog](./federated-catalog/README.md)

These samples focus on the implementation of federated catalogs, covering scenarios such as deploying it as
a standalone runtime or as part of a connector. This also includes demonstration of how to implement a target
node resolver, which resolves the participant connectors in the dataspace and crawls these connectors to compile
a set of all offered catalogs.

All federated catalog samples are located in the [`federated-catalog`](./federated-catalog/README.md) directory.

## Contributing

See [how to contribute](https://github.com/eclipse-edc/docs/blob/main/CONTRIBUTING.md).
Expand Down
11 changes: 9 additions & 2 deletions federated-catalog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ participants in the dataspace. To accomplish this, the FC utilizes crawlers
that periodically crawl the catalogs from each participant and store this list
of catalogs in a local cache.
By maintaining this locally cached version of catalogs, it eliminates the need to query
each participant individually, resulting in faster and more reliable queries.
each participant individually, resulting in faster and more reliable queries. Refer to the
[eclipse-edc/FederatedCatalog](https://github.com/eclipse-edc/FederatedCatalog) for further details.


The following samples shows how to
* implement, build and run different versions of FC e.g.
* standalone,
* embedded.

* implement TargetNodeDirectory and resolve Target Nodes,
* from a static file containing all participants' DSP endpoints,

## Samples

Expand All @@ -38,3 +40,8 @@ a standalone federated catalog will not have the added functionalities of a conn
exposes a catalog API that serves the list of catalogs.

---
### Different Implementations of Node Resolver

### [FC sample 03](./fc-03-static-node-directory/README.md): Resolve Target Catalog Nodes from static participant file
This sample demonstrates a Catalog Node resolver, that implements TargetNodeDirectory. It resolves the Target Catalog
Nodes from a static participant file containing the DSP endpoints of the participants.
7 changes: 4 additions & 3 deletions federated-catalog/fc-00-basic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ Any further dependencies will be added in the later samples based on their use c
### fixed-node-resolver
The Federated Catalog requires a list of Target Catalog Nodes, which are essentially the DSP endpoints of the dataspace participants.
The catalog crawler then crawls these listed endpoints to collect their offered catalogs.
This list of Target Nodes is resolved by a Catalog Node Resolver which implements the [TargetNodeDirectory](https://github.com/eclipse-edc/FederatedCatalog/blob/main/spi/crawler-spi/src/main/java/org/eclipse/edc/crawler/spi/TargetNodeDirectory.java).
Check out [eclipse-edc/FederatedCatalog](https://github.com/eclipse-edc/FederatedCatalog/tree/main) for further information on this topic.
This list of Target Nodes is resolved by a Catalog Node Resolver which implements the `TargetNodeDirectory`.
Check out [eclipse-edc/FederatedCatalog](https://github.com/eclipse-edc/FederatedCatalog) for further information on this topic.


In this module, we've included a fixed Node Resolver, [fixed-node-resolver](./fixed-node-resolver)
Expand All @@ -47,7 +47,8 @@ of the federated catalogs that we are going to build in sample

When the federated catalog boots up, the crawler begins periodically invoking the Target Nodes returned by the
Node Resolver and collecting the catalogs offered by these nodes. To test whether our federated catalogs
(which we will build in later samples: [fc-01-embedded](../fc-01-embedded) and [fc-02-standalone](../fc-02-standalone)) can successfully request and retrieve these catalogs, we need at least one connector with a contract offer.
(which we will build in later samples: [fc-01-embedded](../fc-01-embedded) and [fc-02-standalone](../fc-02-standalone))
can successfully request and retrieve these catalogs, we need at least one connector with a contract offer.

Therefore, in this section, we will start a connector and then create a contract
for this connector. In the future samples, we will refer to it as `participant-connector`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ plugins {
}

dependencies {
implementation(libs.edc.fc.spi.crawler)
runtimeOnly(libs.edc.fc.core)
runtimeOnly(libs.edc.fc.ext.api)
}
Expand Down
139 changes: 139 additions & 0 deletions federated-catalog/fc-03-static-node-directory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Target Node Resolver - Static Node Directory
The Federated Catalog requires a list of Target Catalog Nodes (TCN), which are essentially the participant connectors in the dataspace.
The catalog crawler then crawls the DSP endpoints of these listed nodes, and stores the consolidated set of catalogs in a Federated Catalog Cache (FCC).


This list of Target Catalog Nodes, represented by `TargetNodes`,
is provided by the `TargetNodeDirectory`.
This `TargetNodeDirectory` serves as a 'phone book', maintaining specific information about the
dataspace participants. It accepts an initial list of participants (e.g. list of participants'
IDs), and resolves this input to a list of TargetNodes.

The initial participant list may vary in its source and format depending on specific use cases.
To accommodate these variations, different implementations of the TargetNodeDirectory can be
adapted to customize the resolution process of Target Nodes from the provided participant list.
In this sample, we will build a Catalog Node Resolver that reads the participants' data from a
static file, [participants.json](./target-node-resolver/src/main/resources/participants.json)
and resolves it into TargetNodes.


The code in this sample has been organized into several Java modules:
- `target-node-resolver`: contains `CatalogNodeDirectory`, an implementation of
`TargetNodeDirectory`, which accepts the [`participants.json`](./target-node-resolver/src/main/resources/participants.json)
and returns a list of TargetNodes.
- `embedded|standalone-fc-with-node-resolver`: the embedded/ standalone federated catalog that will be using the `catalog-node-resolver`.


## Implement the Catalog Node Resolver

### Participant file
To keep things straightforward, in this sample we will store our participant list in a static
json file, [participant.json](./target-node-resolver/src/main/resources/participants.json), that contains the `TargetNode`
properties of the dataspace participants.
In this case, the file contains the properties of the `participant-connector` from [fc-00-basic](../fc-00-basic).

```json
{
"name": "https://w3id.org/edc/v0.0.1/ns/",
"id": "provider",
"url": "http://localhost:19194/protocol",
"supportedProtocols": ["dataspace-protocol-http"]
}
```
However, this solution is intended for use only within the sample scope; in production, it must be managed in different way.

### Target Node Resolver

The [CatalogNodeDirectory](./target-node-resolver/src/main/java/org/eclipse/edc/sample/extension/fc/CatalogNodeDirectory.java)
implements TargetNodeDirectory and overrides its `getAll()` method.
In our implementation, this method maps the file content of [`participant.json`](./target-node-resolver/src/main/resources/participants.json)
to a list of TargetNodes.

```java
public class CatalogNodeDirectory implements TargetNodeDirectory {
//...
@Override
public List<TargetNode> getAll() {
try {
return objectMapper.readValue(participantFileContent, new TypeReference<>() {});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//...
}
```
During the preparation phase of a crawler run, the FC ExecutionManager invokes this method
to obtain the list of TargetNodes.
The crawler requests the DSP endpoints of the participants and stores the
aggregated catalogs in a Federated Catalog Cache (FCC).
In this example we are using the in-memory implementation of an FCC.

## Run Federated Catalog with Node Resolver

Previously, we discussed the implementation of standalone and embedded FCs.
In this example, we introduce two separate modules,`standalone-fc-with-node-resolver`
and `embedded-fc-with-node-resolver`, which demonstrate the implementation of each type
of federated catalogs that uses the node resolver`.

Before requesting each of the federated catalog APIs, make sure the `partcipant-connector` that we have set up in the
[fc-00-basic](../fc-00-basic/README.md) is running, and it has a contract offer.

### Run standalone-fc with Node Resolver
Apply the following steps to run a standalone federated catalog that uses the implemented static `target-node-resolver`.

#### Build the standalone-fc JAR
Execute this command in project root:
```bash
./gradlew federated-catalog:fc-03-static-node-directory:standalone-fc-with-node-resolver:build
```

#### Run the standalone-fc

To run the federated catalog, execute the following command

```shell
java -Dedc.fs.config=federated-catalog/fc-02-standalone/standalone-fc/config.properties -jar federated-catalog/fc-03-static-node-directory/standalone-fc-with-node-resolver/build/libs/standalone-fc-with-node-resolver.jar
```

If the execution is successful, then the Catalog API of our standalone FC will listen on port `39195`.

#### Test catalog query API

To get the combined set of catalogs, use the following request:

```http request
curl -d @federated-catalog/fc-01-embedded/resources/empty-query.json \
-H 'content-type: application/json' http://localhost:39195/api/catalog/v1alpha/catalog/query \
-s | jq
```

### Run embedded-FC with Node Resolver
Apply the following steps to run an embedded federated catalog connector that uses the implemented static `target-node-resolver`.

#### Build the fc-connector JAR
Execute this command in project root:

```bash
./gradlew federated-catalog:fc-03-static-node-directory:embedded-fc-with-node-resolver:build
```

#### Run the fc-connector

To run the federated catalog, execute the following command

```shell
java -Dedc.fs.config=federated-catalog/fc-01-embedded/fc-connector/config.properties -jar federated-catalog/fc-03-static-node-directory/embedded-fc-with-node-resolver/build/libs/fc-connector-with-node-resolver.jar
```

If the execution is successful, then the Catalog API of our standalone FC will listen on port `29195`.

#### Test catalog query API

To get the combined set of catalogs, use the following request:

```http request
curl -d @federated-catalog/fc-01-embedded/resources/empty-query.json \
-H 'content-type: application/json' http://localhost:29195/api/catalog/v1alpha/catalog/query \
-s | jq
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Fraunhofer-Gesellschaft
*
* 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:
* Fraunhofer-Gesellschaft - initial API and implementation
*
*/

plugins {
`java-library`
id("application")
alias(libs.plugins.shadow)
}

dependencies {
runtimeOnly(project(":federated-catalog:fc-00-basic:federated-catalog-base"))
runtimeOnly(project(":federated-catalog:fc-03-static-node-directory:target-node-resolver"))

implementation(libs.edc.connector.core)
implementation(libs.edc.control.plane.core)
implementation(libs.edc.configuration.filesystem)
implementation(libs.edc.management.api)
implementation(libs.edc.dsp)
implementation(libs.edc.iam.mock)
implementation(libs.edc.http)
implementation(libs.edc.edr.store.core)

}

application {
mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime")
}

var distTar = tasks.getByName("distTar")
var distZip = tasks.getByName("distZip")

tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
mergeServiceFiles()
archiveFileName.set("fc-connector-with-node-resolver.jar")
dependsOn(distTar, distZip)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 Fraunhofer-Gesellschaft
*
* 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:
* Fraunhofer-Gesellschaft - initial API and implementation
*
*/

plugins {
`java-library`
id("application")
alias(libs.plugins.shadow)
}

dependencies {
runtimeOnly(project(":federated-catalog:fc-00-basic:federated-catalog-base"))
runtimeOnly(project(":federated-catalog:fc-03-static-node-directory:target-node-resolver"))

implementation(libs.edc.connector.core)
runtimeOnly(libs.edc.boot)
runtimeOnly(libs.edc.control.plane.core)
implementation(libs.edc.configuration.filesystem)
runtimeOnly(libs.edc.token.core)
implementation(libs.edc.http)
runtimeOnly(libs.edc.dsp)
implementation(libs.edc.iam.mock)

}


application {
mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime")
}

var distTar = tasks.getByName("distTar")
var distZip = tasks.getByName("distZip")

tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
mergeServiceFiles()
archiveFileName.set("standalone-fc-with-node-resolver.jar")
dependsOn(distTar, distZip)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Fraunhofer-Gesellschaft
*
* 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:
* Fraunhofer-Gesellschaft - initial API and implementation
*
*/

plugins {
`java-library`
id("application")
}

dependencies {
implementation(libs.edc.fc.spi.crawler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2024 Fraunhofer-Gesellschaft
*
* 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:
* Fraunhofer-Gesellschaft - initial API and implementation
*
*/

package org.eclipse.edc.sample.extension.fc;

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 java.io.IOException;
import java.util.List;

public class CatalogNodeDirectory implements TargetNodeDirectory {

private final ObjectMapper objectMapper;
private final String participantFileContent;


public CatalogNodeDirectory(ObjectMapper objectMapper, String participantFileContent) {
this.objectMapper = objectMapper;
this.participantFileContent = participantFileContent;
}

@Override
public List<TargetNode> getAll() {
try {
return objectMapper.readValue(participantFileContent, new TypeReference<>() {});
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void insert(TargetNode targetNode) {

}
}
Loading

0 comments on commit 042b0e0

Please sign in to comment.