Skip to content

Commit

Permalink
add code
Browse files Browse the repository at this point in the history
  • Loading branch information
fvolz committed Oct 17, 2024
1 parent 805b198 commit 100f952
Show file tree
Hide file tree
Showing 14 changed files with 972 additions and 1 deletion.
32 changes: 32 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

name: push

on:
push:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include LICENSE
include README.md
recursive-include *.py
recursive-include *.json
recursive-include *.yaml
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
# fx-python-sdk-cli
# FX Python SDK with CLI

Enter Provider / Consumer FX-Port details in the config files.

Use CLI as provider:
- Get own EDC Assets
- Create EDC Asset for AAS Submodel
- Create generic EDC Access policy
- Create generic EDC Usage policy
- Create generic EDC Contract for specific EDC Asset
- Get EDC policies

Use CLI as consumer:
- Request EDC Assets from Provider
- Request EDRS (after negotiation)
- Request data (requires EDRS)
- Negotiate
- Get Negotiation State (to check after negotiating)
Empty file added __init__.py
Empty file.
13 changes: 13 additions & 0 deletions agreement_body.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/"
},
"@type": "QuerySpec",
"filterExpression": [
{
"operandLeft": "assetId",
"operator": "=",
"operandRight": "sample_object_of_agreement"
}
]
}
21 changes: 21 additions & 0 deletions catalog_request_body.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/",
"cx-taxo": "https://w3id.org/catenax/taxonomy#"
},
"@type": "CatalogRequest",
"counterPartyId": "bpn_placeholder",
"counterPartyAddress": "url_edc_provider_control_plane_base_placeholder",
"protocol": "dataspace-protocol-http",
"querySpec": {
"@type": "QuerySpec",
"filterExpression": [
{
"operandLeft": "'http://purl.org/dc/terms/type'.'@id'",
"operator": "=",
"operandRight": "https://w3id.org/catenax/taxonomy#Submodel"
}
]
}
}
87 changes: 87 additions & 0 deletions cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from consumer import *
from provider import *

def provider_interface():
while True:
print("1: Get own EDC Assets")
print("2: Create EDC Asset for AAS Submodel")
print("3: Create generic EDC Access policy")
print("4: Create generic EDC Usage policy")
print("5: Create generic EDC Contract for specific EDC Asset")
print("6: Get EDC policies")
print("7: Return")
choice = input("Enter your choice: ")
if choice == "1":
print("Getting assets...")
request_own_assets()
elif choice == "2":
asset_id = input("Enter submodel uuid: ")
print("Creating asset for...")
create_edc_asset(asset_id)
elif choice == "3":
print("Creating access policy...")
create_access_policy()
elif choice == "4":
print("Creating usage policy...")
create_usage_policy()
elif choice == "5":
print("Creating contract...")
edc_asset_id = input("Enter EDC asset id: ")
create_contract(edc_asset_id)
elif choice == "6":
print("Getting policies...")
get_policies()
elif choice == "7":
break
else:
print("Invalid choice. Please try again.")

def client_interface():
while True:
print("1: Request EDC Assets from Provider")
print("2: Request EDRS (after negotiation)")
print("3: Request data (requires EDRS)")
print("4: Negotiate")
print("5: Get Negotiation State (check after step 4)")
print("6: Return")
choice = input("Enter your choice: ")
if choice == "1":
print("Requesting assets...")
request_assets_from_provider()
elif choice == "2":
object_of_agreement = input("Enter id of EDC asset: ")
print("Requesting EDRS...")
edrs = get_edrs_for_object(object_of_agreement)
elif choice == "3":
object_of_agreement = input("Enter id of EDC asset: ")
print("Requesting data...")
get_data(edrs)
elif choice == "4":
object_of_agreement = input("Enter id of EDC asset: ")
print("Negotiating...")
edr_negotation_id = negotiate(object_of_agreement)
elif choice == "5":
print("Getting state...")
get_negotiation_state(edr_negotation_id)
elif choice == "6":
break
else:
print("Invalid choice. Please try again.")

def main():
while True:
print("1: Provider")
print("2: Client")
print("3: Exit")
choice = input("Enter your choice: ")
if choice == "1":
provider_interface()
elif choice == "2":
client_interface()
elif choice == "3":
break
else:
print("Invalid choice. Please try again.")

if __name__ == "__main__":
main()
102 changes: 102 additions & 0 deletions consumer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import requests # Apache License 2.0
from requests.auth import HTTPBasicAuth

import base64 # in python
import yaml # MIT
import json

from utils import get_data_offer, offer2et, create_poc_ContractRequest_body
from utils import str_edc_catalog

# --- variables ---
with open('consumer_cfg.yaml', 'r') as file:
consumer_cfg = yaml.safe_load(file)

# - control plane -
url_edc_consumer_control_plane_base = consumer_cfg['consumer-edc-control-plane']['endpoint']
header_control_plane = consumer_cfg['consumer-edc-control-plane']['header'] # this contains secrets, so please use -at least- a secretsmanager instead

# - "identities" -
edc_provider_bpn = consumer_cfg['trusted-providers']['provider_A']['BPN'] # "{{EDCTX-10-1-BPN}}"
url_edc_provider_control_plane_base = consumer_cfg['trusted-providers']['provider_A']['endpoint-control-plane']
# -------------------------------------------------------------------------------------------------------------------
object_of_agreement = 'MB-DSCS'#'simple_test' # we 'magically' know this due to the push notification

def get_edrs_for_object(object_of_agreement):
# see if there are some edrs which have been negotiated for
# Load the JSON from the file
with open('agreement_body.json', 'r') as f:
loaded_agreement_body = json.load(f)

# Replace the placeholder with the actual object of agreement
loaded_agreement_body["filterExpression"][0]["operandRight"] = object_of_agreement

res_catalog_agreement = requests.post(url=url_edc_consumer_control_plane_base + '/management/v2/edrs/request', headers=header_control_plane, json=loaded_agreement_body)
#res_catalog_agreement
res_offer, offer = get_data_offer(res_catalog_agreement.json())
print("Status Offer: "+ str(res_offer))

if res_offer == 0:
res_et, et_dict = offer2et(offer, url_edc_consumer_control_plane_base, header_control_plane)
if res_et == -2:
print("Token Request Failed:" + str(et_dict))
else:
print("Status Endpoint and Token: " + str(res_et))
return et_dict

def get_data(et_dict):
# obtain data:
res_data_info = requests.get(url=et_dict['endpoint'], headers={'Authorization': et_dict['token']})
res_data_info
res_data = requests.get(url=et_dict['endpoint'] + '/$value?extent=WithBlobValue', headers={'Authorization': et_dict['token']})
res_data
element_id = res_data_info.json()['submodelElements'][0]['idShort']
value_enc = res_data.json()[element_id]['value']
val = res_data.json()[res_data_info.json()['submodelElements'][0]['idShort']]['value']
base64.b64decode(val).decode('utf-8')

def request_assets_from_provider():
# obtain all offers from the data provider using a catalog request:
# Load the JSON from the file
with open('catalog_request_body', 'r') as f:
loaded_catalog_request_body = json.load(f)
# Replace the placeholders with actual values
loaded_catalog_request_body["counterPartyId"] = edc_provider_bpn
loaded_catalog_request_body["counterPartyAddress"] = url_edc_provider_control_plane_base + "/api/v1/dsp"
# note: we query against our own EDC (the consumer EDC, who then will negotiate with the target EDC)
res_catalog = requests.post(url=url_edc_consumer_control_plane_base + '/management/v2/catalog/request', headers=header_control_plane, json=loaded_catalog_request_body)
print(str_edc_catalog(res_catalog))
return res_catalog

def negotiate(object_of_agreement):
### Negotiate for Asset
res_catalog = request_assets_from_provider()
# filter offer and endpoint
for dcat_dataset in res_catalog.json()['dcat:dataset']:
# look for the dataset with our id:
if dcat_dataset['@id'] == object_of_agreement:
asset_policy = dcat_dataset['odrl:hasPolicy']
offer_id = asset_policy['@id']

# get negotiation endpoint: dct_endpointUrl
dct_endpointUrl = None
for distribution_method in dcat_dataset['dcat:distribution']:
if distribution_method['dct:format']['@id'] == 'HttpData-PULL':
dct_endpointUrl = distribution_method['dcat:accessService']['dct:endpointUrl']
break
# check if we actuall got the desired endpoint
if dct_endpointUrl is not None:
break

# create request body for the EDR negotiate
edr_negotiation_body = create_poc_ContractRequest_body(dct_endpointUrl, offer_id, edc_provider_bpn, object_of_agreement)
res_edr_negotiation = requests.post(url=url_edc_consumer_control_plane_base + '/management/v2/edrs', headers=header_control_plane, json=edr_negotiation_body)
#res_edr_negotiation
edr_negotation_id = res_edr_negotiation.json()['@id']
return edr_negotation_id # <- necessary to get the state

def get_negotiation_state(edr_negotation_id):
# get the negotiation state:
res_get_edr_negotiation_state = requests.get(url=url_edc_consumer_control_plane_base + '/management/v2/contractnegotiations/' + edr_negotation_id + '/state', headers=header_control_plane)
res_get_edr_negotiation_state
res_get_edr_negotiation_state.json() # <- this should say finalized
9 changes: 9 additions & 0 deletions consumer_cfg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
consumer-edc-control-plane:
endpoint: https://your-edc-control-plane-endpoint
header:
Authorization: Bearer your-authorization-token

trusted-providers:
provider_A:
BPN: your-provider-bpn
endpoint-control-plane: https://your-provider-control-plane-endpoint
Loading

0 comments on commit 100f952

Please sign in to comment.