From 50b404428b2249d13c8363e6e07d8a905e1bbb28 Mon Sep 17 00:00:00 2001 From: schuberr Date: Fri, 27 Sep 2024 11:05:44 +0200 Subject: [PATCH 01/22] enabled AzureStorage in Dashboard --- .gitmodules | 3 +-- EDC-Blockchain-Dashboard | 2 +- blockchain-listener | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index c2fbd7d..f9761c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "EDC-Blockchain-Dashboard"] path = EDC-Blockchain-Dashboard url = https://github.com/GAIA-X4PLC-AAD/EDC-Blockchain-Dashboard.git - branch = demo_v1 + branch = fix/issue53 [submodule "EDC-Blockchain-Interface"] path = EDC-Blockchain-Interface url = https://github.com/GAIA-X4PLC-AAD/EDC-Blockchain-Interface.git @@ -11,4 +11,3 @@ [submodule "blockchain-listener"] path = blockchain-listener url = https://github.com/GAIA-X4PLC-AAD/blockchain-listener.git - branch = fix/startsWithError \ No newline at end of file diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 61b7ff6..2d816e2 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 61b7ff6c4ace032863d8e808cc1e40e868bce71e +Subproject commit 2d816e2c48a25037f6b783200433efd01d3c4767 diff --git a/blockchain-listener b/blockchain-listener index 310d8a0..63d577a 160000 --- a/blockchain-listener +++ b/blockchain-listener @@ -1 +1 @@ -Subproject commit 310d8a066b26149fab3feec2ef3c62077ad4aab7 +Subproject commit 63d577ad7193a42243803a9cbcacaf1939ca6d8e From 4efc100b6ebdba8e33738b051e8b7a6f758eb967 Mon Sep 17 00:00:00 2001 From: schuberr Date: Fri, 27 Sep 2024 11:07:34 +0200 Subject: [PATCH 02/22] bumped edc-version to 0.3.1 (because of StatusChecker-Bug (see https://github.com/eclipse-edc/Connector/pull/3480)), managed some dependencies --- .gitmodules | 1 + EDC-Blockchain-Catalog | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f9761c0..595df66 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,6 +8,7 @@ [submodule "EDC-Blockchain-Catalog"] path = EDC-Blockchain-Catalog url = https://github.com/GAIA-X4PLC-AAD/EDC-Blockchain-Catalog.git + branch = fix/issue53 [submodule "blockchain-listener"] path = blockchain-listener url = https://github.com/GAIA-X4PLC-AAD/blockchain-listener.git diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index 38635e7..a738a44 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit 38635e7a4b8f01be2687ac1193dab412f204b774 +Subproject commit a738a444e4509ba6f04705f9a8198c6f8345c2cb From 58bef2f6fc9e06b5c6a68bdbc3b11b4427a5c462 Mon Sep 17 00:00:00 2001 From: schuberr Date: Fri, 27 Sep 2024 11:33:18 +0200 Subject: [PATCH 03/22] added implementation(libs.edc.control.plane.api.client) --- EDC-Blockchain-Catalog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index a738a44..ff1e979 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit a738a444e4509ba6f04705f9a8198c6f8345c2cb +Subproject commit ff1e9792b8a1672f570638099c0758d31b7cc616 From 374033e3fabeb64f03bd37cf525e0a8282520578 Mon Sep 17 00:00:00 2001 From: schuberr Date: Tue, 1 Oct 2024 11:07:27 +0200 Subject: [PATCH 04/22] added documentation on how to do the data transfer --- README_AssetRegistration_Transfer.md | 265 +++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 README_AssetRegistration_Transfer.md diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md new file mode 100644 index 0000000..75171b6 --- /dev/null +++ b/README_AssetRegistration_Transfer.md @@ -0,0 +1,265 @@ +# Introduction +This documentation shows howto register an asset in the EDC and howto transfer an asset from one EDC to another. + +The samples are the minimum definition of data that is possible. They can be enhanced for specific requirements. + +# Infrastructure +## EDCs +There are two EDCs running in the msg infrastructure for testing purposes. The EDCs are running in the following URLs: +- https://edcdb-pr.gxfs.gx4fm.org/ (used as Producer) +- https://edcdb-co.gxfs.gx4fm.org/ (used as Consumer) + +To access the EDCs, you need to have an API key (`x-api-key`). The API key is used as a header parameter in the requests. The API key is the same for both EDCs and can be requested from the msg / TUB team. + +## Azure Storages +Those EDCs have associated azure storages both for the asset registration (on producer side) and for the data transfer (on consumer side). + +### Storage and Containers +Name of the azure storage for asset registration: `msgedcstorage` +Available containers: +- `src-container` +- `dest-container` +- `uc1-src` +- `uc1-dest` +- `uc2-src` +- `uc2-dest` +- `uc3-src` +- `uc3-dest` + +> NOTE: +Please use the correct container for the asset registration (`*-src`) and data transfer (`*-dest`) that fits to your use case. + + +# Asset registration +## Upload an asset to the azure storage +TODO +pip install azure-storage-blob + + +## Create asset + +## Create policy + +## Create contract + +# Data Transfer +## Retrieve contract information +### Base URL +`https://edc-pr.gxfs.gx4fm.org/management` +### Endpoint +`/v2/contractdefinitions/:id` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Accept: application/json` +### Method +`GET` +### Input payload +No payload +### Example response +```json +{ + "@id": "contract6", + "@type": "edc:ContractDefinition", + "edc:accessPolicyId": "1", + "edc:contractPolicyId": "1", + "edc:assetsSelector": { + "@type": "edc:Criterion", + "edc:operandLeft": "https://w3id.org/edc/v0.0.1/ns/id", + "edc:operator": "=", + "edc:operandRight": "bullwhip_result1" + }, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- The value of `:id` must be replaced with the value of the desired contract-id (normally stored in the self-description). +- The value of `edc:assetsSelector` -> `"edc:operandRight"` of a `"edc:operandLeft": "https://w3id.org/edc/v0.0.1/ns/id"` is the value that must be used in the next steps (`{{assetNameFull}}`). + +## Contract negotiation +### Base URL +`https://edc-co.gxfs.gx4fm.org/management` +### Endpoint +`/v2/contractnegotiations` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Content-Type: application/json` +`Accept: application/json` +### Method +`POST` +### Input payload +```json +{ + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "odrl": "http://www.w3.org/ns/odrl/2/" + }, + "@type": "NegotiationInitiateRequestDto", + "connectorId": "edc_pr", + "connectorAddress": "https://edc-pr.gxfs.gx4fm.org/api/v1/dsp", + "consumerId": "edc_co", + "providerId": "edc_pr", + "protocol": "dataspace-protocol-http", + "offer": { + "offerId": "{{contractDefinitionId}}:{{assetNameFull}}:{{$randomUUID}}", + "assetId": "{{assetNameFull}}", + "policy": { + "@type": "Set", + "odrl:permission": [], + "odrl:prohibition": [], + "odrl:obligation": [], + "odrl:target": "{{assetNameFull}}" + } + } +} +``` +### Example response +``` +{ + "@type": "edc:IdResponse", + "@id": "0ba52804-732f-4857-98b7-cd47f27d930f", + "edc:createdAt": 1727708368814, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- Be aware of the variables (e.g. `{{assetNameFull}}`) in the payload. They must be replaced with the correct values. +- Take the value of `@id` from the response and use it in the next steps (`{{ContractNegotiationUID}}`) + +## Retrieve agreement +### Base URL +`https://edc-co.gxfs.gx4fm.org/management` +### Endpoint +`/v2/contractnegotiations/:id` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Accept: application/json` +### Method +`GET` +### Input payload +No payload +### Example response +```json +{ + "@type": "edc:ContractNegotiation", + "@id": "0ba52804-732f-4857-98b7-cd47f27d930f", + "edc:type": "CONSUMER", + "edc:protocol": "dataspace-protocol-http", + "edc:state": "FINALIZED", + "edc:counterPartyId": "edc_pr", + "edc:counterPartyAddress": "https://edc-pr.gxfs.gx4fm.org/api/v1/dsp", + "edc:callbackAddresses": [], + "edc:createdAt": 1727708368814, + "edc:contractAgreementId": "d375dc66-2704-498b-9e43-b77fefd85d1f", + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- The value of `:id` must be replaced with the value of `@id` from the previous step (`{{ContractNegotiationUID}}`). +- The value of `edc:contractAgreementId` is the value that must be used in the next steps (`{{ContractAgreementUID}}`). +- The state should be `FINALIZED`. + +## Initiate data transfer +### Base URL +`https://edc-co.gxfs.gx4fm.org/management` +### Endpoint +`/v2/transferprocesses` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Content-Type: application/json` +`Accept: application/json` +### Method +`POST` +### Input payload +```json +{ + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/" + }, + "assetId": "{{assetNameFull}}", + "connectorId": "edc_co", + "contractId": "{{ContractAgreementUID}}", + "dataDestination": { + "type": "AzureStorage", + "properties": { + "container": "dest-container", + "account": "msgedcstorage", + "keyName": "msgedcstorage-sas" + } + }, + "@type": "TransferRequestDto", + "connectorAddress": "https://edc-pr.gxfs.gx4fm.org/api/v1/dsp", + "managedResources": false, + "protocol": "dataspace-protocol-http" +} +``` +### Example response +```json +{ + "@type": "edc:IdResponse", + "@id": "f02626b2-1424-44b7-8ed0-3d76273b6360", + "edc:createdAt": 1727708594759, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- The value of `@id` must be used in the next step (`{{transferprocessId}}`). + +## Check transfer status +### Base URL +`https://edc-co.gxfs.gx4fm.org/management` +### Endpoint +`/v2/transferprocesses/:id/state` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Accept: application/json` +### Method +`GET` +### Input payload +No payload +### Example response +```json +{ + "@type": "edc:TransferState", + "edc:state": "STARTED", + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- The value of `:id` must be replaced with the value of `@id` from the previous step (`{{transferprocessId}}`). +> NOTE: the current EDC version has a bug in setting the correct transfer status. The status remains in `STARTED` even if the transfer is completed. This will be fixed with a deployment of a newer version of the EDC. +> \ No newline at end of file From 89e274258f8358c1876edcf5bf0867fac673f8b6 Mon Sep 17 00:00:00 2001 From: schuberr Date: Tue, 1 Oct 2024 11:13:24 +0200 Subject: [PATCH 05/22] typo --- README_AssetRegistration_Transfer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 75171b6..16e5434 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -42,7 +42,7 @@ pip install azure-storage-blob ## Create contract -# Data Transfer +# Data transfer ## Retrieve contract information ### Base URL `https://edc-pr.gxfs.gx4fm.org/management` From fb74ddfeb1b7fac47de9d8d13afc8b143c905a09 Mon Sep 17 00:00:00 2001 From: schuberr Date: Tue, 1 Oct 2024 12:53:47 +0200 Subject: [PATCH 06/22] added bulletpoint for contracts --- README_AssetRegistration_Transfer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 16e5434..69b87e3 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -81,6 +81,7 @@ No payload ### Hints - The value of `:id` must be replaced with the value of the desired contract-id (normally stored in the self-description). - The value of `edc:assetsSelector` -> `"edc:operandRight"` of a `"edc:operandLeft": "https://w3id.org/edc/v0.0.1/ns/id"` is the value that must be used in the next steps (`{{assetNameFull}}`). +- The value of `@id` is the value that must be used in the next steps (`{{contractDefinitionId}}`). ## Contract negotiation ### Base URL From 3b803c773b61eaf91b0cb41068bc54dd08d69910 Mon Sep 17 00:00:00 2001 From: schuberr Date: Tue, 1 Oct 2024 16:41:43 +0200 Subject: [PATCH 07/22] added documentation how to register assets in the EDC and the Azure Storage --- README_AssetRegistration_Transfer.md | 172 ++++++++++++++++++++-- scripts/java/ListFilesInContainer.java | 46 ++++++ scripts/java/README.md | 30 ++++ scripts/java/UploadAsset.java | 63 ++++++++ scripts/js/README.md | 5 + scripts/js/list_files_in_container.js | 30 ++++ scripts/js/upload_asset.js | 42 ++++++ scripts/python/README.md | 31 ++++ scripts/python/list_files_in_container.py | 41 ++++++ scripts/python/upload_asset.py | 59 ++++++++ 10 files changed, 508 insertions(+), 11 deletions(-) create mode 100644 scripts/java/ListFilesInContainer.java create mode 100644 scripts/java/README.md create mode 100644 scripts/java/UploadAsset.java create mode 100644 scripts/js/README.md create mode 100644 scripts/js/list_files_in_container.js create mode 100644 scripts/js/upload_asset.js create mode 100644 scripts/python/README.md create mode 100644 scripts/python/list_files_in_container.py create mode 100644 scripts/python/upload_asset.py diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 69b87e3..3a45fe7 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -3,6 +3,8 @@ This documentation shows howto register an asset in the EDC and howto transfer a The samples are the minimum definition of data that is possible. They can be enhanced for specific requirements. +This documentation is based on the EDC version 0.3.1 and expects that the claim compliance provider (CCP) extension is running (see (here)[https://github.com/GAIA-X4PLC-AAD/EDC-Blockchain-Catalog/tree/main/extensions/claim-compliance-provider-integration]. + # Infrastructure ## EDCs There are two EDCs running in the msg infrastructure for testing purposes. The EDCs are running in the following URLs: @@ -11,36 +13,184 @@ There are two EDCs running in the msg infrastructure for testing purposes. The E To access the EDCs, you need to have an API key (`x-api-key`). The API key is used as a header parameter in the requests. The API key is the same for both EDCs and can be requested from the msg / TUB team. -## Azure Storages +## Azure Storage Those EDCs have associated azure storages both for the asset registration (on producer side) and for the data transfer (on consumer side). ### Storage and Containers Name of the azure storage for asset registration: `msgedcstorage` -Available containers: -- `src-container` -- `dest-container` +Available containers for asset registration: - `uc1-src` -- `uc1-dest` - `uc2-src` -- `uc2-dest` - `uc3-src` + +Available containers for data transfer: +- `uc1-dest` +- `uc2-dest` - `uc3-dest` > NOTE: Please use the correct container for the asset registration (`*-src`) and data transfer (`*-dest`) that fits to your use case. +To upload an asset to the azure storage, you need to have a **SAS token**. The SAS token can be requested from the msg team. +> NOTE: Please handle the SAS token with care! Do not share it with unauthorized persons. # Asset registration ## Upload an asset to the azure storage -TODO -pip install azure-storage-blob +To programmatically upload an asset to the `msgedcstorage` you can use the provided **python** scripts. The scripts are located in the `scripts` directory next to this README file. +There you can also find programming examples for **java** and **JavaScript**. -## Create asset +Please refer to the readme files in the `scripts` directory for more information. -## Create policy +## Create asset in the provider EDC +### Base URL +`https://edcdb-pr.gxfs.gx4fm.org/management` +### Endpoint +`/v3/assets` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Content-Type: application/json` +`Accept: application/json` +### Method +`POST` +### Input payload +```json +{ + "@id": "{{assetNameFull}}", + "properties": { + "name": "{{assetNameFull}}", + "version": "1.0", + "contenttype": "text/json", + "claimComplianceProviderResponse": "", + "claimsList": "{{listOfClaimsAsBase64EncodedString}}", + "gxParticipantCredentials": "{{listOfParticipantCredentialsAsBase64EncodedString}}" + }, + "dataAddress": { + "type": "AzureStorage", + "name": "{{assetNameFull}}", + "account": "msgedcstorage", + "container": "{{AzureContainerID}}", + "blobname": "{{Filename}}", + "keyName": "msgedcstorage-key1" + }, + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/" + } +} +``` +### Example response +```json +{ + "@type": "edc:IdResponse", + "@id": "bullwhip_result1", + "edc:createdAt": 1727772367110, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- Note that you also can create an asset via the EDC UI (https://edcdb-pr.gxfs.gx4fm.org). +- Make sure to replace the variables in the payload with the correct values. -## Create contract +## Create policy in the provider EDC +### Base URL +`https://edcdb-pr.gxfs.gx4fm.org/management` +### Endpoint +`/v2/policydefinitions` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Content-Type: application/json` +`Accept: application/json` +### Method +`POST` +### Input payload +```json +{ + "policy": { + "@type": "set", + "@context": "http://www.w3.org/ns/odrl.jsonld" + }, + "@id": "{{policyId}}", + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/" + } +} +``` +### Example response +```json +{ + "@type": "edc:IdResponse", + "@id": "1", + "edc:createdAt": 1727772413817, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- Note that you also can create an asset via the EDC UI (https://edcdb-pr.gxfs.gx4fm.org). +- Make sure to replace the variables in the payload with the correct values. +- Note that this is a very simple policy. You might want to extend it for your specific requirements. + +## Create contract in the provider EDC +### Base URL +`https://edcdb-pr.gxfs.gx4fm.org/management` +### Endpoint +`v2/contractdefinitions` +### Header parameters +`x-api-key: {{API-KEY}}}` +`Content-Type: application/json` +`Accept: application/json` +### Method +`POST` +### Input payload +```json +{ + "@id": "{{contractDefinitionId}}", + "assetsSelector": [ + { + "operandLeft": "https://w3id.org/edc/v0.0.1/ns/id", + "operator": "=", + "operandRight": "{{assetNameFull}}" + } + ], + "accessPolicyId": "{{policyId}}", + "contractPolicyId": "{{policyId}}", + "@context": { + "edc": "https://w3id.org/edc/v0.0.1/ns/" + } +} +``` +### Example response +```json +{ + "@type": "edc:IdResponse", + "@id": "contract6", + "edc:createdAt": 1727772440000, + "@context": { + "tuberlin": "https://ise.tu.berlin/edc/v0.0.1/ns/", + "dct": "https://purl.org/dc/terms/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + } +} +``` +### Hints +- Note that you also can create an asset via the EDC UI (https://edcdb-pr.gxfs.gx4fm.org). +- Make sure to replace the variables in the payload with the correct values. # Data transfer ## Retrieve contract information diff --git a/scripts/java/ListFilesInContainer.java b/scripts/java/ListFilesInContainer.java new file mode 100644 index 0000000..3776a79 --- /dev/null +++ b/scripts/java/ListFilesInContainer.java @@ -0,0 +1,46 @@ +import com.azure.storage.blob.*; +import com.azure.storage.blob.models.BlobItem; + +public class ListFilesInContainer { + + public static void listFilesInContainer(String sasToken, String containerName) { + try { + // Create a Blob Service Client + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder() + .endpoint("https://msgedcstorage.blob.core.windows.net") + .sasToken(sasToken) + .buildClient(); + + // Create a Container Client + BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName); + + // List all blobs in the container + System.out.println("Files in container '" + containerName + "':"); + for (BlobItem blobItem : containerClient.listBlobs()) { + System.out.println(blobItem.getName()); + } + } catch (Exception e) { + System.out.println("Error listing files in the container: " + e.getMessage()); + } + } + + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("Usage: java ListFilesInContainer "); + System.exit(100); + } + + // Container name from command line argument + String containerName = args[0]; + + // Retrieve SAS token from environment variable + String sasToken = System.getenv("SAS_TOKEN"); + if (sasToken == null) { + System.out.println("SAS token not found in environment variable SAS_TOKEN."); + System.exit(101); + } + + // List files in the container + listFilesInContainer(sasToken, containerName); + } +} \ No newline at end of file diff --git a/scripts/java/README.md b/scripts/java/README.md new file mode 100644 index 0000000..fd3d0eb --- /dev/null +++ b/scripts/java/README.md @@ -0,0 +1,30 @@ +# Introduction +This directory shows how to access azure storage with java in a very basic way. + +# Getting Started +You may want to use this as a starting point for your own project by reusing the code provided in this directory. + +> Note to replace the `System.out.println("...")` with proper logging. + +## Usage +### Prerequisites +#### Obtain SAS-Token from the msg team and set it as environment variable +```bash +export SAS_TOKEN= +``` +### List of all files in a given container +Implemented in the `ListFilesInContainer` class. +### Upload a file to a given container in azure storage +Implemented in the `UploadAsset` class. + +# Dependency +You will need to import the azure-storage-blob dependency into your project: +```xml + + + com.azure + azure-storage-blob + 12.14.2 + +``` + diff --git a/scripts/java/UploadAsset.java b/scripts/java/UploadAsset.java new file mode 100644 index 0000000..f3db9f2 --- /dev/null +++ b/scripts/java/UploadAsset.java @@ -0,0 +1,63 @@ +import com.azure.storage.blob.*; +import com.azure.storage.blob.models.BlobStorageException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Paths; + +public class UploadAsset { + + public static void uploadFileToBlob(String filePath, String sasToken, String containerName) { + try { + // Extract the file name from the file path + String fileName = Paths.get(filePath).getFileName().toString(); + + // Create a Blob Service Client + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder() + .endpoint("https://msgedcstorage.blob.core.windows.net") + .sasToken(sasToken) + .buildClient(); + + // Create a Blob Client + BlobClient blobClient = blobServiceClient.getBlobContainerClient(containerName).getBlobClient(fileName); + + // Upload the file + System.out.println("Uploading file " + fileName + " to the container..."); + blobClient.upload(new FileInputStream(new File(filePath)), new File(filePath).length(), true); + + System.out.println("File " + filePath + " uploaded successfully."); + } catch (BlobStorageException | IOException e) { + System.out.println("Error uploading the file: " + e.getMessage()); + } + } + + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Usage: java UploadAsset "); + System.exit(100); + } + + // Path to the file to be uploaded + String filePath = args[0]; + + // Container name from command line argument + String containerName = args[1]; + + // Retrieve SAS token from environment variable + String sasToken = System.getenv("SAS_TOKEN"); + if (sasToken == null) { + System.out.println("SAS token not found in environment variable SAS_TOKEN."); + System.exit(101); + } + + // Check if the file path exists + if (!new File(filePath).exists()) { + System.out.println("File path does not exist: " + filePath); + System.exit(102); + } + + // Upload the file to the container + uploadFileToBlob(filePath, sasToken, containerName); + } +} \ No newline at end of file diff --git a/scripts/js/README.md b/scripts/js/README.md new file mode 100644 index 0000000..cf44133 --- /dev/null +++ b/scripts/js/README.md @@ -0,0 +1,5 @@ +# Introduction +Please refer to the following README file for more information: +- [Java README](../java/README.md) + + diff --git a/scripts/js/list_files_in_container.js b/scripts/js/list_files_in_container.js new file mode 100644 index 0000000..163b0b4 --- /dev/null +++ b/scripts/js/list_files_in_container.js @@ -0,0 +1,30 @@ +const { BlobServiceClient } = require('@azure/storage-blob'); + +async function listFilesInContainer(sasToken, containerName) { + try { + const blobServiceClient = new BlobServiceClient(`https://msgedcstorage.blob.core.windows.net?${sasToken}`); + const containerClient = blobServiceClient.getContainerClient(containerName); + + console.log(`Files in container '${containerName}':`); + for await (const blob of containerClient.listBlobsFlat()) { + console.log(blob.name); + } + } catch (error) { + console.error(`Error listing files in the container: ${error.message}`); + } +} + +if (process.argv.length !== 3) { + console.log('Usage: node list_files_in_container.js '); + process.exit(100); +} + +const containerName = process.argv[2]; +const sasToken = process.env.SAS_TOKEN; + +if (!sasToken) { + console.error('SAS token not found in environment variable SAS_TOKEN.'); + process.exit(101); +} + +listFilesInContainer(sasToken, containerName); diff --git a/scripts/js/upload_asset.js b/scripts/js/upload_asset.js new file mode 100644 index 0000000..b69d661 --- /dev/null +++ b/scripts/js/upload_asset.js @@ -0,0 +1,42 @@ +const { BlobServiceClient } = require('@azure/storage-blob'); +const fs = require('fs'); +const path = require('path'); + +async function uploadFileToBlob(filePath, sasToken, containerName) { + try { + const fileName = path.basename(filePath); + + const blobServiceClient = new BlobServiceClient(`https://msgedcstorage.blob.core.windows.net?${sasToken}`); + const containerClient = blobServiceClient.getContainerClient(containerName); + const blobClient = containerClient.getBlobClient(fileName); + + console.log(`Uploading file ${fileName} to the container...`); + const data = fs.readFileSync(filePath); + await blobClient.uploadData(data); + + console.log(`File ${filePath} uploaded successfully.`); + } catch (error) { + console.error(`Error uploading the file: ${error.message}`); + } +} + +if (process.argv.length !== 4) { + console.log('Usage: node upload_asset.js '); + process.exit(100); +} + +const filePath = process.argv[2]; +const containerName = process.argv[3]; +const sasToken = process.env.SAS_TOKEN; + +if (!sasToken) { + console.error('SAS token not found in environment variable SAS_TOKEN.'); + process.exit(101); +} + +if (!fs.existsSync(filePath)) { + console.error(`File path does not exist: ${filePath}`); + process.exit(102); +} + +uploadFileToBlob(filePath, sasToken, containerName); \ No newline at end of file diff --git a/scripts/python/README.md b/scripts/python/README.md new file mode 100644 index 0000000..ec08daf --- /dev/null +++ b/scripts/python/README.md @@ -0,0 +1,31 @@ +# Introduction +This directory shows how to access azure storage with python. + +# Getting Started +You may want to use this as a starting point for your own project by reusing the code provided in this directory, or you can call the python scripts directly from your local machine. + +> Note that the scripts are linked to the azure storage account `msgedcstorage`. You can change the storage account in the scripts if you want to use a different storage account. + +## Usage +### Prerequisites +#### Install the azure-storage-blob package +```bash +pip install azure-storage-blob +``` +#### Obtain SAS-Token from msg and set it as environment variable +```bash +export SAS_TOKEN= +``` +### List of all files in a given container +```bash +python list_files_in_container.py +``` +### Upload a file to a given container in azure storage +```bash +python upload_asset.py +``` +> Note that the file will not be updated once the file already exists. + + + + diff --git a/scripts/python/list_files_in_container.py b/scripts/python/list_files_in_container.py new file mode 100644 index 0000000..c29c8f8 --- /dev/null +++ b/scripts/python/list_files_in_container.py @@ -0,0 +1,41 @@ +from azure.storage.blob import BlobServiceClient +import os +import sys + + +# Function to list all files in the Blob storage container +def list_files_in_container(sas_token, container_name): + try: + # Create a Blob Service Client + blob_service_client = BlobServiceClient(account_url="https://msgedcstorage.blob.core.windows.net", + credential=sas_token) + + # Create a Container Client + container_client = blob_service_client.get_container_client(container_name) + + # List all blobs in the container + blobs_list = container_client.list_blobs() + print(f"Files in container '{container_name}':") + for blob in blobs_list: + print(blob.name) + + except Exception as e: + print(f"Error listing files in the container: {e}") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python list_files.py ") + sys.exit(100) + + # Container name from command line argument + container_name = sys.argv[1] + + # Retrieve SAS token from environment variable + sas_token = os.getenv('SAS_TOKEN') + if not sas_token: + print("SAS token not found in environment variable SAS_TOKEN.") + sys.exit(101) + + # List files in the container + list_files_in_container(sas_token, container_name) diff --git a/scripts/python/upload_asset.py b/scripts/python/upload_asset.py new file mode 100644 index 0000000..c05c4c2 --- /dev/null +++ b/scripts/python/upload_asset.py @@ -0,0 +1,59 @@ +from azure.storage.blob import BlobServiceClient +from azure.core.exceptions import ResourceExistsError +import os +import sys + + +# Function to upload a file to the Blob storage +def upload_file_to_blob(file_path, sas_token, container_name): + try: + # Extract the file name from the file_path + file_name = os.path.basename(file_path) + + # Create a Blob Service Client + blob_service_client = BlobServiceClient(account_url="https://msgedcstorage.blob.core.windows.net", + credential=sas_token) + + # Create a Container Client + container_client = blob_service_client.get_container_client(container_name) + + # Create a Blob Client + blob_client = container_client.get_blob_client(blob=file_name) + + # Upload the file + print(f"Uploading file {file_name} to the container...") + with open(file_path, "rb") as data: + blob_client.upload_blob(data) + + print(f"File {file_path} uploaded successfully.") + + except ResourceExistsError: + print("The file already exists in the container.") + except Exception as e: + print(f"Error uploading the file: {e}") + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python upload_asset.py ") + sys.exit(100) + + # Path to the file to be uploaded + file_path = sys.argv[1] + + # Container name from command line argument + container_name = sys.argv[2] + + # Retrieve SAS token from environment variable + sas_token = os.getenv('SAS_TOKEN') + if not sas_token: + print("SAS token not found in environment variable SAS_TOKEN.") + sys.exit(101) + + # Check if the file_path exists + if not os.path.exists(file_path): + print(f"File path does not exist: {file_path}") + sys.exit(102) + + # Upload the file to the container + upload_file_to_blob(file_path, sas_token, container_name) From ef996c72dabce32c7e69675dc59bec9470cc0318 Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 10:47:53 +0200 Subject: [PATCH 08/22] removed disabled state from container field. --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 2d816e2..c3b802c 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 2d816e2c48a25037f6b783200433efd01d3c4767 +Subproject commit c3b802c6f420669108c27baa386ca163dbed889c From 4990d75133f7d93646503678a57a2069e063c72e Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 13:24:10 +0200 Subject: [PATCH 09/22] refactored http calls. JSON can only be parsed when the request was successful. On http error >=400 the payload is html normally so no json can be parsed. Improved log messages. --- EDC-Blockchain-Catalog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index ff1e979..d808cd7 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit ff1e9792b8a1672f570638099c0758d31b7cc616 +Subproject commit d808cd70c9020de3f548339ca97ab1ba08d6308c From 4b64336d0b23ef588d3d71288d72b726fa1be573 Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 15:12:41 +0200 Subject: [PATCH 10/22] fixed base urls --- README_AssetRegistration_Transfer.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 3a45fe7..10bf69b 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -44,7 +44,7 @@ Please refer to the readme files in the `scripts` directory for more information ## Create asset in the provider EDC ### Base URL -`https://edcdb-pr.gxfs.gx4fm.org/management` +`https://edc-pr.gxfs.gx4fm.org/management` ### Endpoint `/v3/assets` ### Header parameters @@ -100,7 +100,7 @@ Please refer to the readme files in the `scripts` directory for more information ## Create policy in the provider EDC ### Base URL -`https://edcdb-pr.gxfs.gx4fm.org/management` +`https://edc-pr.gxfs.gx4fm.org/management` ### Endpoint `/v2/policydefinitions` ### Header parameters @@ -145,7 +145,7 @@ Please refer to the readme files in the `scripts` directory for more information ## Create contract in the provider EDC ### Base URL -`https://edcdb-pr.gxfs.gx4fm.org/management` +`https://edc-pr.gxfs.gx4fm.org/management` ### Endpoint `v2/contractdefinitions` ### Header parameters From 9cb54fc4ef06f005bc8a366d229dc4ea17a2c2bf Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 15:19:05 +0200 Subject: [PATCH 11/22] set the originator field for both azure and httpdata --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index c3b802c..4e9d3bf 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit c3b802c6f420669108c27baa386ca163dbed889c +Subproject commit 4e9d3bf9d44c0b30d94403863f89e9642ee70b3a From 89404111b76b645d34e1b14c4e3f0ded6a847924 Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 15:31:03 +0200 Subject: [PATCH 12/22] added missing MatAutocompleteModule --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 4e9d3bf..6d4ea3a 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 4e9d3bf9d44c0b30d94403863f89e9642ee70b3a +Subproject commit 6d4ea3aecda7de70d67d2ed1a5944c1c86eadf28 From 4fbf27978b469d495b0e445903d4113b53a27527 Mon Sep 17 00:00:00 2001 From: schuberr Date: Wed, 2 Oct 2024 15:50:46 +0200 Subject: [PATCH 13/22] added missing MatAutocompleteModule imports --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 6d4ea3a..98b7b32 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 6d4ea3aecda7de70d67d2ed1a5944c1c86eadf28 +Subproject commit 98b7b32109d78caaf13a5264fd004098227fa9e3 From b84e4b6671a62d5454b021fe08227ad8ce8e4173 Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 7 Oct 2024 09:23:25 +0200 Subject: [PATCH 14/22] added originator field to asset creation payload --- README_AssetRegistration_Transfer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 10bf69b..2cbb54c 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -61,6 +61,7 @@ Please refer to the readme files in the `scripts` directory for more information "name": "{{assetNameFull}}", "version": "1.0", "contenttype": "text/json", + "originator": "https://edc-pr.gxfs.gx4fm.org/api/v1/dsp", "claimComplianceProviderResponse": "", "claimsList": "{{listOfClaimsAsBase64EncodedString}}", "gxParticipantCredentials": "{{listOfParticipantCredentialsAsBase64EncodedString}}" From d2abf62db49446ed85c3af3830a3f2c2652254ad Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 7 Oct 2024 18:13:28 +0200 Subject: [PATCH 15/22] set provider and consumer id into transfermessage from contract aggreement. --- EDC-Blockchain-Catalog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index d808cd7..744add1 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit d808cd70c9020de3f548339ca97ab1ba08d6308c +Subproject commit 744add1d6537df82ddc8e10b13c21564d1f0f9d0 From cc8d793d67696b11dc188337fdde2f17d1c1e6f4 Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 7 Oct 2024 18:26:18 +0200 Subject: [PATCH 16/22] changed connectorId to edc_pr in `/v2/transferprocesses` --- README_AssetRegistration_Transfer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_AssetRegistration_Transfer.md b/README_AssetRegistration_Transfer.md index 2cbb54c..33b89a5 100644 --- a/README_AssetRegistration_Transfer.md +++ b/README_AssetRegistration_Transfer.md @@ -349,7 +349,7 @@ No payload "edc": "https://w3id.org/edc/v0.0.1/ns/" }, "assetId": "{{assetNameFull}}", - "connectorId": "edc_co", + "connectorId": "edc_pr", "contractId": "{{ContractAgreementUID}}", "dataDestination": { "type": "AzureStorage", From 2a7c5c6d27946b6605779c80c58a09ee2fd4782e Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 7 Oct 2024 18:43:11 +0200 Subject: [PATCH 17/22] changed consumer edc id to consumer edc id stored in contract aggreement (ownConnectorId would have a uuid in its name) --- EDC-Blockchain-Catalog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index 744add1..da7b780 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit 744add1d6537df82ddc8e10b13c21564d1f0f9d0 +Subproject commit da7b7808946f1a9a4e9935ef87aca373dcabe874 From 47bd1715fc5f05dc3676dcc0e32bb0b01516f5bc Mon Sep 17 00:00:00 2001 From: schuberr Date: Tue, 8 Oct 2024 17:23:23 +0200 Subject: [PATCH 18/22] added implementation(libs.edc.control.plane.api) --- EDC-Blockchain-Catalog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Catalog b/EDC-Blockchain-Catalog index da7b780..fba4efb 160000 --- a/EDC-Blockchain-Catalog +++ b/EDC-Blockchain-Catalog @@ -1 +1 @@ -Subproject commit da7b7808946f1a9a4e9935ef87aca373dcabe874 +Subproject commit fba4efbd062328603ddc0bf175dcd8809bcddcbd From 84b538b33c040fe243b8325b38ec6f4f885d3683 Mon Sep 17 00:00:00 2001 From: schuberr Date: Thu, 10 Oct 2024 14:19:17 +0200 Subject: [PATCH 19/22] changed edc name to the value of participant id of the EDC (edc_pr) --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 98b7b32..48acbcf 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 98b7b32109d78caaf13a5264fd004098227fa9e3 +Subproject commit 48acbcf1c0303e75a6052de3503b9112442433f3 From 159dd77a416aabda29ef4b417fdc07619a0756cc Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 14 Oct 2024 11:50:33 +0200 Subject: [PATCH 20/22] corrected edc management url --- EDC-Blockchain-Dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDC-Blockchain-Dashboard b/EDC-Blockchain-Dashboard index 48acbcf..16aa788 160000 --- a/EDC-Blockchain-Dashboard +++ b/EDC-Blockchain-Dashboard @@ -1 +1 @@ -Subproject commit 48acbcf1c0303e75a6052de3503b9112442433f3 +Subproject commit 16aa788acf174278dff22171404e29b194977f91 From ee94cd0e3ae1cfba9276cbb25ce23dd5b45e2a51 Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 14 Oct 2024 13:43:05 +0200 Subject: [PATCH 21/22] added example for powershell --- scripts/java/README.md | 5 +++++ scripts/python/README.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/scripts/java/README.md b/scripts/java/README.md index fd3d0eb..803773e 100644 --- a/scripts/java/README.md +++ b/scripts/java/README.md @@ -9,9 +9,14 @@ You may want to use this as a starting point for your own project by reusing the ## Usage ### Prerequisites #### Obtain SAS-Token from the msg team and set it as environment variable +bash: ```bash export SAS_TOKEN= ``` +powershell: +```powers +$env:SAS_TOKEN="" +``` ### List of all files in a given container Implemented in the `ListFilesInContainer` class. ### Upload a file to a given container in azure storage diff --git a/scripts/python/README.md b/scripts/python/README.md index ec08daf..b7a15ec 100644 --- a/scripts/python/README.md +++ b/scripts/python/README.md @@ -13,9 +13,14 @@ You may want to use this as a starting point for your own project by reusing the pip install azure-storage-blob ``` #### Obtain SAS-Token from msg and set it as environment variable +bash: ```bash export SAS_TOKEN= ``` +powershell: +```powers +$env:SAS_TOKEN="" +``` ### List of all files in a given container ```bash python list_files_in_container.py From d7124d924c42f93b18bd1c166165338c8c0da296 Mon Sep 17 00:00:00 2001 From: schuberr Date: Mon, 14 Oct 2024 14:04:24 +0200 Subject: [PATCH 22/22] added double quotes to the export --- scripts/java/README.md | 2 +- scripts/python/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/java/README.md b/scripts/java/README.md index 803773e..bac7613 100644 --- a/scripts/java/README.md +++ b/scripts/java/README.md @@ -11,7 +11,7 @@ You may want to use this as a starting point for your own project by reusing the #### Obtain SAS-Token from the msg team and set it as environment variable bash: ```bash -export SAS_TOKEN= +export SAS_TOKEN="" ``` powershell: ```powers diff --git a/scripts/python/README.md b/scripts/python/README.md index b7a15ec..db8a5de 100644 --- a/scripts/python/README.md +++ b/scripts/python/README.md @@ -15,7 +15,7 @@ pip install azure-storage-blob #### Obtain SAS-Token from msg and set it as environment variable bash: ```bash -export SAS_TOKEN= +export SAS_TOKEN="" ``` powershell: ```powers