From b79ff55b6d039557cab00f7c2fc16cb5fc9f1d75 Mon Sep 17 00:00:00 2001
From: david zynda
Date: Wed, 20 Dec 2023 16:07:46 +0100
Subject: [PATCH 01/23] WiP: sign/decline option
---
src/assets/MOCK/IMR18650V1.json | 745 +++++++++++---------
src/assets/MOCK/search.json | 60 ++
src/components/general/LoadingComponent.vue | 1 +
src/components/general/StepperItem.vue | 124 ++++
src/services/BackendService.js | 26 +-
src/store/index.js | 75 +-
src/views/PassportView.vue | 9 +-
7 files changed, 688 insertions(+), 352 deletions(-)
create mode 100644 src/assets/MOCK/search.json
diff --git a/src/assets/MOCK/IMR18650V1.json b/src/assets/MOCK/IMR18650V1.json
index 6af879090..28ae2b84e 100644
--- a/src/assets/MOCK/IMR18650V1.json
+++ b/src/assets/MOCK/IMR18650V1.json
@@ -1,406 +1,475 @@
{
- "status" : 200,
- "data" : {
- "metadata" : {
- "contract" : {
- "@id" : "9b3c0977-6b14-4201-bd76-55f681a92872",
- "@type" : "dcat:Dataset",
- "odrl:hasPolicy" : {
- "@id" : "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
- "@type" : "odrl:Set",
- "odrl:permission" : [ ],
- "odrl:prohibition" : [ ],
- "odrl:obligation" : [ ],
- "odrl:target" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
+ "status": 200,
+ "data": {
+ "metadata": {
+ "contract": {
+ "@id": "9b3c0977-6b14-4201-bd76-55f681a92872",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": {
+ "@id": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
+ "@type": "odrl:Set",
+ "odrl:permission": [
+ {
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e4684",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "ID 3.0 Trace"
+ }
+ }
+ },
+ {
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e4684",
+ "odrl:action": {
+ "odrl:type": "ACCESS"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "DPP"
+ }
+ }
+ }
+ ],
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
},
- "dcat:distribution" : [ {
- "@type" : "dcat:Distribution",
- "dct:format" : {
- "@id" : "HttpProxy"
- },
- "dcat:accessService" : "1795254a-e354-46c7-9d88-04608b05ca9f"
- }, {
- "@type" : "dcat:Distribution",
- "dct:format" : {
- "@id" : "AmazonS3"
+ "dcat:distribution": [
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
},
- "dcat:accessService" : "1795254a-e354-46c7-9d88-04608b05ca9f"
- } ],
- "edc:description" : "Battery Passport test data",
- "edc:id" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
+ }
+ ],
+ "edc:description": "Battery Passport test data",
+ "edc:id": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
},
- "negotiation" : {
- "init" : {
- "request" : {
- "@context" : {
- "odrl" : "http://www.w3.org/ns/odrl/2/"
+ "negotiation": {
+ "init": {
+ "request": {
+ "@context": {
+ "odrl": "http://www.w3.org/ns/odrl/2/"
},
- "@type" : "NegotiationInitiateRequestDto",
- "connectorAddress" : "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
- "protocol" : "dataspace-protocol-http",
- "connectorId" : "BPNL00000000CBA5",
- "offer" : {
- "offerId" : "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
- "assetId" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
- "policy" : {
- "@type" : "odrl:Set",
- "odrl:permission" : [ ],
- "odrl:prohibition" : [ ],
- "odrl:obligation" : [ ],
- "odrl:target" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
+ "@type": "NegotiationInitiateRequestDto",
+ "connectorAddress": "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
+ "protocol": "dataspace-protocol-http",
+ "connectorId": "BPNL00000000CBA5",
+ "offer": {
+ "offerId": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
+ "assetId": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
+ "policy": {
+ "@type": "odrl:Set",
+ "odrl:permission": [],
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
}
}
},
- "response" : {
- "@id" : "b8a41089-1eac-462a-b8e8-e40f724b302f",
- "@type" : "edc:IdResponseDto",
- "edc:createdAt" : 1688393629505,
- "@context" : {
- "dct" : "https://purl.org/dc/terms/",
- "tx" : "https://w3id.org/tractusx/v0.0.1/ns/",
- "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/"
+ "response": {
+ "@id": "b8a41089-1eac-462a-b8e8-e40f724b302f",
+ "@type": "edc:IdResponseDto",
+ "edc:createdAt": 1688393629505,
+ "@context": {
+ "dct": "https://purl.org/dc/terms/",
+ "tx": "https://w3id.org/tractusx/v0.0.1/ns/",
+ "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/"
}
}
},
- "get" : {
- "response" : {
- "@id" : "b8a41089-1eac-462a-b8e8-e40f724b302f",
- "@type" : "edc:ContractNegotiationDto",
- "edc:type" : "CONSUMER",
- "edc:protocol" : "dataspace-protocol-http",
- "edc:state" : "FINALIZED",
- "edc:counterPartyAddress" : "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
- "edc:callbackAddresses" : [ ],
- "edc:contractAgreementId" : "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390",
- "@context" : {
- "dct" : "https://purl.org/dc/terms/",
- "tx" : "https://w3id.org/tractusx/v0.0.1/ns/",
- "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/"
+ "get": {
+ "response": {
+ "@id": "b8a41089-1eac-462a-b8e8-e40f724b302f",
+ "@type": "edc:ContractNegotiationDto",
+ "edc:type": "CONSUMER",
+ "edc:protocol": "dataspace-protocol-http",
+ "edc:state": "FINALIZED",
+ "edc:counterPartyAddress": "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
+ "edc:callbackAddresses": [],
+ "edc:contractAgreementId": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390",
+ "@context": {
+ "dct": "https://purl.org/dc/terms/",
+ "tx": "https://w3id.org/tractusx/v0.0.1/ns/",
+ "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/"
}
}
}
},
- "transfer" : {
- "init" : {
- "request" : {
- "@context" : {
- "odrl" : "http://www.w3.org/ns/odrl/2/"
+ "transfer": {
+ "init": {
+ "request": {
+ "@context": {
+ "odrl": "http://www.w3.org/ns/odrl/2/"
},
- "assetId" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
- "connectorAddress" : "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
- "contractId" : "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390",
- "dataDestination" : {
- "properties" : {
- "type" : "HttpProxy"
+ "assetId": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
+ "connectorAddress": "https://materialpass.dev.demo.catena-x.net/BPNL000000000000/api/v1/dsp",
+ "contractId": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390",
+ "dataDestination": {
+ "properties": {
+ "type": "HttpProxy"
}
},
- "managedResources" : false,
- "privateProperties" : {
- "receiverHttpEndpoint" : "https://materialpass.dev.demo.catena-x.net/endpoint/bb8d235e-96c1-4fa3-a9ea-2ec71255ed18"
+ "managedResources": false,
+ "privateProperties": {
+ "receiverHttpEndpoint": "https://materialpass.dev.demo.catena-x.net/endpoint/bb8d235e-96c1-4fa3-a9ea-2ec71255ed18"
},
- "protocol" : "dataspace-protocol-http",
- "transferType" : {
- "contentType" : "application/octet-stream",
- "isFinite" : true
+ "protocol": "dataspace-protocol-http",
+ "transferType": {
+ "contentType": "application/octet-stream",
+ "isFinite": true
}
},
- "response" : {
- "@id" : "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
- "@type" : "edc:IdResponseDto",
- "edc:createdAt" : 1688393631831,
- "@context" : {
- "dct" : "https://purl.org/dc/terms/",
- "tx" : "https://w3id.org/tractusx/v0.0.1/ns/",
- "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/"
+ "response": {
+ "@id": "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
+ "@type": "edc:IdResponseDto",
+ "edc:createdAt": 1688393631831,
+ "@context": {
+ "dct": "https://purl.org/dc/terms/",
+ "tx": "https://w3id.org/tractusx/v0.0.1/ns/",
+ "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/"
}
}
},
- "get" : {
- "response" : {
- "@id" : "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
- "@type" : "edc:TransferProcessDto",
- "edc:state" : "COMPLETED",
- "edc:stateTimestamp" : 1688393633142,
- "edc:type" : "CONSUMER",
- "edc:callbackAddresses" : [ ],
- "edc:dataDestination" : {
- "edc:type" : "HttpProxy"
+ "get": {
+ "response": {
+ "@id": "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
+ "@type": "edc:TransferProcessDto",
+ "edc:state": "COMPLETED",
+ "edc:stateTimestamp": 1688393633142,
+ "edc:type": "CONSUMER",
+ "edc:callbackAddresses": [],
+ "edc:dataDestination": {
+ "edc:type": "HttpProxy"
},
- "edc:dataRequest" : {
- "@id" : "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
- "@type" : "edc:DataRequestDto",
- "edc:assetId" : "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
- "edc:contractId" : "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390"
+ "edc:dataRequest": {
+ "@id": "b9ab18f2-b624-4e2c-a922-c2e727467bb2",
+ "@type": "edc:DataRequestDto",
+ "edc:assetId": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918",
+ "edc:contractId": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:102bae7a-22de-44b2-9ed5-ca5f62023390"
},
- "edc:receiverHttpEndpoint" : "https://materialpass.dev.demo.catena-x.net/endpoint/bb8d235e-96c1-4fa3-a9ea-2ec71255ed18",
- "@context" : {
- "dct" : "https://purl.org/dc/terms/",
- "tx" : "https://w3id.org/tractusx/v0.0.1/ns/",
- "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/"
+ "edc:receiverHttpEndpoint": "https://materialpass.dev.demo.catena-x.net/endpoint/bb8d235e-96c1-4fa3-a9ea-2ec71255ed18",
+ "@context": {
+ "dct": "https://purl.org/dc/terms/",
+ "tx": "https://w3id.org/tractusx/v0.0.1/ns/",
+ "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/"
}
}
}
}
},
- "passport" : {
- "electrochemicalProperties" : {
- "ratedCapacity" : 94,
- "batteryEnergy" : {
- "energyRoundtripEfficiencyChange" : 48.2,
- "maximumAllowedBatteryEnergy" : 85000,
- "energyRoundtripEfficiency" : 25
+ "passport": {
+ "electrochemicalProperties": {
+ "ratedCapacity": 94,
+ "batteryEnergy": {
+ "energyRoundtripEfficiencyChange": 48.2,
+ "maximumAllowedBatteryEnergy": 85000,
+ "energyRoundtripEfficiency": 25
},
- "ratioMaximumAllowedBatteryPowerAndMaximumAllowedBatteryEnergy" : 0.588,
- "batteryVoltage" : {
- "nominalVoltage" : 3.7,
- "maxVoltage" : 4.2,
- "minVoltage" : 2.5
+ "ratioMaximumAllowedBatteryPowerAndMaximumAllowedBatteryEnergy": 0.588,
+ "batteryVoltage": {
+ "nominalVoltage": 3.7,
+ "maxVoltage": 4.2,
+ "minVoltage": 2.5
},
- "internalResistance" : {
- "cellinternalResistance" : 0.36,
- "packinternalResistanceIncrease" : 1,
- "packinternalResistance" : 100
+ "internalResistance": {
+ "cellinternalResistance": 0.36,
+ "packinternalResistanceIncrease": 1,
+ "packinternalResistance": 100
},
- "capacityThresholdExhaustion" : 23,
- "batteryPower" : {
- "powerFade" : 23,
- "originalPowerCapability" : -1.7976931348623157E308,
- "originalPowerCapabilityLimits" : -1.7976931348623157E308,
- "maximumAllowedBatteryPower" : -1.7976931348623157E308,
- "powerCapabilityAt20Charge" : -1.7976931348623157E308,
- "powerCapabilityAt80Charge" : -1.7976931348623157E308
+ "capacityThresholdExhaustion": 23,
+ "batteryPower": {
+ "powerFade": 23,
+ "originalPowerCapability": -1.7976931348623157E308,
+ "originalPowerCapabilityLimits": -1.7976931348623157E308,
+ "maximumAllowedBatteryPower": -1.7976931348623157E308,
+ "powerCapabilityAt20Charge": -1.7976931348623157E308,
+ "powerCapabilityAt80Charge": -1.7976931348623157E308
},
- "capacityFade" : 1.55
+ "capacityFade": 1.55
},
- "document" : {
- "responsibleSourcing" : [ {
- "title" : "2021 Responsible Sourcing document",
- "fileLocation" : null
- } ],
- "packagingInstructions" : [ {
- "title" : "Packing Instruction v.2.0",
- "fileLocation" : null
- } ],
- "transportationInstructions" : [ {
- "title" : "Transport manual",
- "fileLocation" : null
- } ],
- "vehicleDismantlingProcedure" : [ {
- "title" : "Car dismantling manual",
- "fileLocation" : "http://www.ietf.org/rfc/rfc2396.txt"
- } ],
- "testReportsResults" : [ {
- "title" : "Battery Test Reports",
- "fileLocation" : "http://www.Batterytestreports.de"
- } ],
- "batteryDismantlingProcedure" : [ {
- "title" : "Dismantling Manual",
- "fileLocation" : "http://www.dissmantlingmanual.org"
- } ],
- "safetyMeasures" : [ {
- "title" : "Safety Instruction",
- "fileLocation" : "http://www.safetyinstructions.txt"
- } ],
- "declarationOfConformity" : [ {
- "title" : "Declaration of Conformity No. 3",
- "fileLocation" : null
- } ]
+ "document": {
+ "responsibleSourcing": [
+ {
+ "title": "2021 Responsible Sourcing document",
+ "fileLocation": null
+ }
+ ],
+ "packagingInstructions": [
+ {
+ "title": "Packing Instruction v.2.0",
+ "fileLocation": null
+ }
+ ],
+ "transportationInstructions": [
+ {
+ "title": "Transport manual",
+ "fileLocation": null
+ }
+ ],
+ "vehicleDismantlingProcedure": [
+ {
+ "title": "Car dismantling manual",
+ "fileLocation": "http://www.ietf.org/rfc/rfc2396.txt"
+ }
+ ],
+ "testReportsResults": [
+ {
+ "title": "Battery Test Reports",
+ "fileLocation": "http://www.Batterytestreports.de"
+ }
+ ],
+ "batteryDismantlingProcedure": [
+ {
+ "title": "Dismantling Manual",
+ "fileLocation": "http://www.dissmantlingmanual.org"
+ }
+ ],
+ "safetyMeasures": [
+ {
+ "title": "Safety Instruction",
+ "fileLocation": "http://www.safetyinstructions.txt"
+ }
+ ],
+ "declarationOfConformity": [
+ {
+ "title": "Declaration of Conformity No. 3",
+ "fileLocation": null
+ }
+ ]
},
- "datePlacedOnMarket" : "27.04.2022",
- "cellChemistry" : {
- "electrolyteComposition" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "dimethyl carbonate (DCM)"
- } ],
- "anodeCompositionOther" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "Carboxymethyl cellulose"
- } ],
- "recyclateContentActiveMaterials" : [ {
- "materialPercentageMassFraction" : 6,
- "materialWeight" : null,
- "materialName" : "Ni/2021/PlantE"
- }, {
- "materialPercentageMassFraction" : 4,
- "materialWeight" : null,
- "materialName" : "Li/2021/PlantE"
- }, {
- "materialPercentageMassFraction" : 0,
- "materialWeight" : null,
- "materialName" : "Pb(battery model does not contain Pb)"
- }, {
- "materialPercentageMassFraction" : 0,
- "materialWeight" : null,
- "materialName" : "Co(battery model does not contain Pb)"
- } ],
- "anodeActiveMaterials" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "Graphite"
- } ],
- "cathodeActiveMaterials" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "LiMn2O4 Lithium Manganese Oxide"
- } ],
- "cathodeCompositionOther" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "binder:PVDF"
- } ]
+ "datePlacedOnMarket": "27.04.2022",
+ "cellChemistry": {
+ "electrolyteComposition": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "dimethyl carbonate (DCM)"
+ }
+ ],
+ "anodeCompositionOther": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "Carboxymethyl cellulose"
+ }
+ ],
+ "recyclateContentActiveMaterials": [
+ {
+ "materialPercentageMassFraction": 6,
+ "materialWeight": null,
+ "materialName": "Ni/2021/PlantE"
+ },
+ {
+ "materialPercentageMassFraction": 4,
+ "materialWeight": null,
+ "materialName": "Li/2021/PlantE"
+ },
+ {
+ "materialPercentageMassFraction": 0,
+ "materialWeight": null,
+ "materialName": "Pb(battery model does not contain Pb)"
+ },
+ {
+ "materialPercentageMassFraction": 0,
+ "materialWeight": null,
+ "materialName": "Co(battery model does not contain Pb)"
+ }
+ ],
+ "anodeActiveMaterials": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "Graphite"
+ }
+ ],
+ "cathodeActiveMaterials": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "LiMn2O4 Lithium Manganese Oxide"
+ }
+ ],
+ "cathodeCompositionOther": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "binder:PVDF"
+ }
+ ]
},
- "physicalDimensions" : {
- "length" : 2000,
- "width" : 1000,
- "weight" : 3500,
- "diameter" : null,
- "height" : 200
+ "physicalDimensions": {
+ "length": 2000,
+ "width": 1000,
+ "weight": 3500,
+ "diameter": null,
+ "height": 200
},
- "temperatureRangeIdleState" : {
- "temperatureRangeIdleStateUpperLimit" : 50,
- "temperatureRangeIdleStateLowerLimit" : -20
+ "temperatureRangeIdleState": {
+ "temperatureRangeIdleStateUpperLimit": 50,
+ "temperatureRangeIdleStateLowerLimit": -20
},
- "batteryCycleLife" : {
- "cycleLifeTestCRate" : 2,
- "cycleLifeTestDepthOfDischarge" : 1.8,
- "expectedLifetime" : 2500
+ "batteryCycleLife": {
+ "cycleLifeTestCRate": 2,
+ "cycleLifeTestDepthOfDischarge": 1.8,
+ "expectedLifetime": 2500
},
- "manufacturer" : {
- "name" : "CompanyE",
- "contact" : {
- "faxNumber" : "+49 89 0987654324",
- "website" : "https://www.CompanyE.com",
- "phoneNumber" : "+49 89 1234567893",
- "email" : "companyE@company.com"
+ "manufacturer": {
+ "name": "CompanyE",
+ "contact": {
+ "faxNumber": "+49 89 0987654324",
+ "website": "https://www.CompanyE.com",
+ "phoneNumber": "+49 89 1234567893",
+ "email": "companyE@company.com"
},
- "address" : {
- "locality" : {
- "value" : "CityE",
- "technicalKey" : "BLOCK"
+ "address": {
+ "locality": {
+ "value": "CityE",
+ "technicalKey": "BLOCK"
},
- "country" : {
- "shortName" : "Germany"
+ "country": {
+ "shortName": "Germany"
},
- "postCode" : {
- "value" : "65-250E",
- "technicalKey" : "CEDEX"
+ "postCode": {
+ "value": "65-250E",
+ "technicalKey": "CEDEX"
},
- "thoroughfare" : {
- "value" : "StreetE",
- "number" : "1",
- "technicalKey" : "STREET"
+ "thoroughfare": {
+ "value": "StreetE",
+ "number": "1",
+ "technicalKey": "STREET"
},
- "premise" : {
- "value" : null,
- "technicalKey" : "BUILDING"
+ "premise": {
+ "value": null,
+ "technicalKey": "BUILDING"
},
- "postalDeliveryPoint" : {
- "value" : null,
- "technicalKey" : "intERURBAN_DELIVERY_POint"
+ "postalDeliveryPoint": {
+ "value": null,
+ "technicalKey": "intERURBAN_DELIVERY_POint"
}
}
},
- "warrantyPeriod" : "96",
- "composition" : {
- "compositionOfBattery" : [ {
- "materialPercentageMassFraction" : null,
- "materialWeight" : null,
- "materialName" : "Separator: PE"
- } ],
- "criticalRawMaterials" : "Lithium, Natural graphite",
- "components" : {
- "componentsPartNumber" : "Voltage cables",
- "componentsSupplier" : [ {
- "componentsSupplierName" : "AB Corporation",
- "address" : {
- "locality" : {
- "value" : "CityF",
- "technicalKey" : "BLOCK"
- },
- "country" : {
- "shortName" : "Germany"
- },
- "postCode" : {
- "value" : "65-250F",
- "technicalKey" : "CEDEX"
- },
- "thoroughfare" : {
- "value" : "StreetF",
- "number" : "1",
- "technicalKey" : "STREET"
- },
- "premise" : {
- "value" : "PlantF",
- "technicalKey" : "BUILDING"
+ "warrantyPeriod": "96",
+ "composition": {
+ "compositionOfBattery": [
+ {
+ "materialPercentageMassFraction": null,
+ "materialWeight": null,
+ "materialName": "Separator: PE"
+ }
+ ],
+ "criticalRawMaterials": "Lithium, Natural graphite",
+ "components": {
+ "componentsPartNumber": "Voltage cables",
+ "componentsSupplier": [
+ {
+ "componentsSupplierName": "AB Corporation",
+ "address": {
+ "locality": {
+ "value": "CityF",
+ "technicalKey": "BLOCK"
+ },
+ "country": {
+ "shortName": "Germany"
+ },
+ "postCode": {
+ "value": "65-250F",
+ "technicalKey": "CEDEX"
+ },
+ "thoroughfare": {
+ "value": "StreetF",
+ "number": "1",
+ "technicalKey": "STREET"
+ },
+ "premise": {
+ "value": "PlantF",
+ "technicalKey": "BUILDING"
+ },
+ "postalDeliveryPoint": {
+ "value": null,
+ "technicalKey": "INTERURBAN_DELIVERY_POINT"
+ }
},
- "postalDeliveryPoint" : {
- "value" : null,
- "technicalKey" : "INTERURBAN_DELIVERY_POINT"
+ "contact": {
+ "faxNumber": "+49 89 0987654324",
+ "website": "https://www.companyF.com",
+ "phoneNumber": "+49 89 1234567893",
+ "email": "companyF@companyF.com"
}
- },
- "contact" : {
- "faxNumber" : "+49 89 0987654324",
- "website" : "https://www.companyF.com",
- "phoneNumber" : "+49 89 1234567893",
- "email" : "companyF@companyF.com"
}
- } ]
+ ]
}
},
- "manufacturing" : {
- "dateOfManufacturing" : "2022-01-24",
- "address" : {
- "locality" : {
- "value" : "CityE",
- "technicalKey" : "BLOCK"
+ "manufacturing": {
+ "dateOfManufacturing": "2022-01-24",
+ "address": {
+ "locality": {
+ "value": "CityE",
+ "technicalKey": "BLOCK"
},
- "country" : {
- "shortName" : "Germany"
+ "country": {
+ "shortName": "Germany"
},
- "postCode" : {
- "value" : "65-250E",
- "technicalKey" : "CEDEX"
+ "postCode": {
+ "value": "65-250E",
+ "technicalKey": "CEDEX"
},
- "thoroughfare" : {
- "value" : "StreetE",
- "number" : "1",
- "technicalKey" : "STREET"
+ "thoroughfare": {
+ "value": "StreetE",
+ "number": "1",
+ "technicalKey": "STREET"
},
- "premise" : {
- "value" : "PlantE",
- "technicalKey" : "BUILDING"
+ "premise": {
+ "value": "PlantE",
+ "technicalKey": "BUILDING"
},
- "postalDeliveryPoint" : {
- "value" : "GateE",
- "technicalKey" : "INTERURBAN_DELIVERY_POINT"
+ "postalDeliveryPoint": {
+ "value": "GateE",
+ "technicalKey": "INTERURBAN_DELIVERY_POINT"
}
}
},
- "batteryIdentification" : {
- "batteryType" : "Lithium-Manganese-Oxide (LMO)",
- "batteryIDDMCCode" : "IMR18650V1",
- "batteryModel" : "Pi4 Orionis"
+ "batteryIdentification": {
+ "batteryType": "Lithium-Manganese-Oxide (LMO)",
+ "batteryIDDMCCode": "IMR18650V1",
+ "batteryModel": "Pi4 Orionis"
},
- "stateOfBattery" : {
- "stateOfHealth" : 20,
- "statusBattery" : "first life",
- "stateOfCharge" : 50
+ "stateOfBattery": {
+ "stateOfHealth": 20,
+ "statusBattery": "first life",
+ "stateOfCharge": 50
},
- "cO2FootprintTotal" : 210
+ "cO2FootprintTotal": 210
}
}
}
\ No newline at end of file
diff --git a/src/assets/MOCK/search.json b/src/assets/MOCK/search.json
new file mode 100644
index 000000000..0b31abcde
--- /dev/null
+++ b/src/assets/MOCK/search.json
@@ -0,0 +1,60 @@
+{
+ "status": 200,
+ "data": {
+ "id": "ededdca7-998d-4cd6-94a1-dd1e24025a85",
+ "token": "37da8e38e4dc14d32c0edfa82a0ea94bbf39e4013c010bfdfececdc58eadf0c5",
+ "contract": {
+ "@id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": {
+ "@id": "MDc4NjZjMTQtOGE0YS00ZjQ3LWEyYzgtMWI5MmEwOGQ1NTlm:dXJuOnV1aWQ6MGVjOGNmMmItZjU4ZS0zZjEzLWI1ZWYtZTdkZDAxZDE1YjE5:ZDJhNGZjMDItNjc0MS00NGUzLWE5MjUtODNjZDJkMDc0MTk0",
+ "@type": "odrl:Set",
+ "odrl:permission": {
+ "odrl:target": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": [
+ {
+ "odrl:leftOperand": "Membership",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ },
+ {
+ "odrl:leftOperand": "FrameworkAgreement.sustainability",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ }
+ ]
+ }
+ },
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ },
+ "dcat:distribution": [
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "f3276bfe-7337-46f8-863f-75661717fb59"
+ },
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "f3276bfe-7337-46f8-863f-75661717fb59"
+ }
+ ],
+ "edc:description": "Digital Product Passport (DPP) test data",
+ "edc:id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/general/LoadingComponent.vue b/src/components/general/LoadingComponent.vue
index 4ca3ef6dd..82a916f80 100644
--- a/src/components/general/LoadingComponent.vue
+++ b/src/components/general/LoadingComponent.vue
@@ -91,6 +91,7 @@
this.stepsNames.contractNegotiation.initialStepSubtitle
"
displayId
+ contractSign
:idLabel="
statusData.data.history['negotiation-accepted']
? statusData.data.history['negotiation-accepted'].status
diff --git a/src/components/general/StepperItem.vue b/src/components/general/StepperItem.vue
index 089b5e874..c112b0c8e 100644
--- a/src/components/general/StepperItem.vue
+++ b/src/components/general/StepperItem.vue
@@ -42,6 +42,57 @@
{{ condition ? idLabel : "" }}
+
+
+
+
+ Choose a policy
+
+
+
+
+
+ Decline
+ Agree
+
+
+
+
@@ -50,6 +101,10 @@
+
+
diff --git a/src/services/BackendService.js b/src/services/BackendService.js
index 4eb2ed229..0743354c8 100644
--- a/src/services/BackendService.js
+++ b/src/services/BackendService.js
@@ -173,32 +173,32 @@ export default class BackendService {
retries++;
}
let history = jsonUtil.get("data.history", statusResponse);
- if(jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)){
+ if (jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)) {
return await this.retrievePassport(negotiation, authentication);
}
// Get status again
statusResponse = await this.getStatus(processId, authentication)
status = jsonUtil.get("data.status", statusResponse);
history = jsonUtil.get("data.history", statusResponse);
- if(jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)){
+ if (jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)) {
return await this.retrievePassport(negotiation, authentication);
}
retries = 0;
// Until the transfer is completed or the status is failed
- while(retries < maxRetries){
+ while (retries < maxRetries) {
// Wait
await threadUtil.sleep(waitingTime);
// Refresh the values
statusResponse = await this.getStatus(processId, authentication);
status = jsonUtil.get("data.status", statusResponse);
history = jsonUtil.get("data.history", statusResponse);
- if((jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)) || status === "FAILED"){
+ if ((jsonUtil.exists("transfer-completed", history) && jsonUtil.exists("data-received", history)) || status === "FAILED") {
break;
}
retries++;
}
-
+
// If the status is failed...
if (status === "FAILED") {
return this.getErrorMessage(
@@ -210,7 +210,7 @@ export default class BackendService {
// If is not failed return the passport
return await this.retrievePassport(negotiation, authentication);
}
-
+
getErrorMessage(message, status, statusText) {
return {
"message": message,
@@ -302,6 +302,17 @@ export default class BackendService {
});
});
}
+ async acceptContract(contractToSign) {
+ return new Promise(resolve => {
+ axios.post(`${BACKEND_URL}/api/data`, contractToSign)
+ .then((response => {
+ resolve(response.data);
+ })
+ .catch(error => {
+ console.error('Error accepting contract', error);
+ }))
+ });
+ }
async retrievePassport(negotiation, authentication) {
return new Promise(resolve => {
let body = this.getRequestBody(negotiation);
@@ -336,7 +347,6 @@ export default class BackendService {
} else {
resolve(e.message)
}
-
});
});
}
@@ -345,6 +355,8 @@ export default class BackendService {
let body = this.getSearchBody(id, processId);
axios.post(`${BACKEND_URL}/api/contract/search`, body, this.getHeaders(authentication))
.then((response) => {
+ // Setting the status to the Store state
+ store.commit('setSearchData', response.data);
resolve(response.data);
})
.catch((e) => {
diff --git a/src/store/index.js b/src/store/index.js
index 8e4c16ad0..b474f9c7c 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -55,6 +55,68 @@ export default createStore({
]
}
],
+ searchData: {
+ "contract": {
+ "@id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": {
+ "@id": "MDc4NjZjMTQtOGE0YS00ZjQ3LWEyYzgtMWI5MmEwOGQ1NTlm:dXJuOnV1aWQ6MGVjOGNmMmItZjU4ZS0zZjEzLWI1ZWYtZTdkZDAxZDE1YjE5:N2ZjNWNjM2UtMjhjZC00OWI5LTg3MjItZTQ5ODk2MDM1ZDBk",
+ "@type": "odrl:Set",
+ "odrl:permission": [{
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e0000",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "ID 3.0 Trace"
+ }
+ }
+ },
+
+ {
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e1111",
+ "odrl:action": {
+ "odrl:type": "ACCESS"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "DPP"
+ }
+ }
+ }],
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ },
+ "dcat:distribution": [{
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
+ }, {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
+ }],
+ "edc:description": "Digital Product Passport (DPP) test data",
+ "edc:id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ },
+ "id": "a4a816d9-86af-4abd-bffc-d1964698d39b",
+ "token": "5e2e1afd6bdca53de86368b9884b67b0755f4653c5b31cb5b6bc9f070a043672"
+ },
+ contractToSign: null,
processId: null,
searchContractId: null,
irsState: false
@@ -78,14 +140,25 @@ export default createStore({
getIrsState(state) {
return state.irsState;
},
+ getSearchData(state) {
+ return state.searchData;
+ },
+ getContractToSign(state) {
+ return state.contractToSign;
+ },
},
mutations: {
+ setSearchData(state, newSearchData) {
+ state.searchData = newSearchData;
+ },
+ setContractToSign(state, newContractToSign) {
+ state.contractToSign = newContractToSign;
+ },
setEmail(state, newEmail) {
state.email = newEmail;
},
setPassword(state, newPassword) {
state.password = newPassword;
-
},
setClientId(state, clientId) {
let bytes = CryptoJS.AES.encrypt(clientId, state.sessionId);
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index f90d7b7fb..03ccfcc86 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -225,14 +225,14 @@ export default {
irsData: [],
processId: null,
backendService: null,
- error: true,
+ error: false,
errorObj: {
title: "Something went wrong while returning the passport!",
description: "We are sorry for that, you can retry or try again later",
type: "error",
status: 500,
statusText: "Internal Server Error",
- }
+ },
};
},
@@ -316,10 +316,7 @@ export default {
// Init backendService
// Get access token from IDP
// Get the aspect for the selected version
- response = await this.backendService.getPassport(
- id,
- this.auth
- );
+ response = await this.backendService.getPassport(id, this.auth);
} catch (e) {
console.log("passportView.getPassport() -> " + e);
this.errorObj.title = jsonUtil.exists("message", response)
From 0c0139074d33e11e6d9776a7962fb398c6c08803 Mon Sep 17 00:00:00 2001
From: david zynda
Date: Mon, 15 Jan 2024 13:48:15 +0100
Subject: [PATCH 02/23] modal responsivness
---
.../components/general/contractModal.scss | 44 +++++++++++++++++++
src/assets/styles/main.scss | 1 +
src/components/general/StepperItem.vue | 17 +------
3 files changed, 46 insertions(+), 16 deletions(-)
create mode 100644 src/assets/styles/components/general/contractModal.scss
diff --git a/src/assets/styles/components/general/contractModal.scss b/src/assets/styles/components/general/contractModal.scss
new file mode 100644
index 000000000..60c34f086
--- /dev/null
+++ b/src/assets/styles/components/general/contractModal.scss
@@ -0,0 +1,44 @@
+/**
+ * Catena-X - Product Passport Consumer Frontend
+ *
+ * Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.
+ *
+ * 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 govern in permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.contract-modal {
+ justify-content: center;
+ align-items: center;
+ .content-container {
+ margin-top: 20px;
+ width: 300px;
+ }
+ .contract-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center !important;
+ align-items: center !important;
+ padding: 20px 80px 50px 80px;
+ }
+
+ .title-container {
+ width: fit-content;
+ font-size: 20px;
+ font-weight: 600;
+ padding: 15px;
+ }
+}
diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss
index c0c32b439..a048c5067 100644
--- a/src/assets/styles/main.scss
+++ b/src/assets/styles/main.scss
@@ -28,6 +28,7 @@
@import "components/general/header";
@import "components/general/search";
@import "components/general/tooltip";
+@import "components/general/contractModal";
@import "components/general/dialog";
@import "components/general/recursiveTree";
@import "components/passport/documentField";
diff --git a/src/components/general/StepperItem.vue b/src/components/general/StepperItem.vue
index c112b0c8e..e036fa517 100644
--- a/src/components/general/StepperItem.vue
+++ b/src/components/general/StepperItem.vue
@@ -45,9 +45,7 @@
-
- Choose a policy
-
+ Choose a policy
-
-
From cc7e7c6f4e9356f31a747292f1f7f43bfb676027 Mon Sep 17 00:00:00 2001
From: david zynda
Date: Mon, 29 Jan 2024 09:57:43 +0100
Subject: [PATCH 03/23] json viewer lib + policies extractor
---
package-lock.json | 87 ++++++++
package.json | 2 +
src/assets/MOCK/dpp.json | 70 +++++-
.../components/general/contractModal.scss | 15 ++
src/components/general/StepperItem.vue | 112 +++++++---
src/main.js | 5 +-
src/store/index.js | 206 +++++++++++++-----
7 files changed, 411 insertions(+), 86 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index feb75ea98..fcb909ebf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@popperjs/core": "^2.11.2",
"@vitejs/plugin-vue": "^4.0.0",
"axios": "^0.26.0",
+ "clipboard": "^2.0.11",
"core-js": "^3.8.3",
"crypto-js": "^4.2.0",
"eslint-config-prettier": "^8.5.0",
@@ -23,6 +24,7 @@
"vite-plugin-vuetify": "^1.0.2",
"vue": "^3.2.47",
"vue-router": "^4.0.13",
+ "vue3-json-viewer": "^2.2.2",
"vue3-qrcode-reader": "^0.0.1",
"vuetify": "^3.1.4",
"vuex": "^4.0.2",
@@ -982,6 +984,16 @@
"node": ">= 6"
}
},
+ "node_modules/clipboard": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+ "dependencies": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -1104,6 +1116,11 @@
"node": ">=8"
}
},
+ "node_modules/delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -1795,6 +1812,14 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+ "dependencies": {
+ "delegate": "^3.1.2"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
@@ -2633,6 +2658,11 @@
"version": "2.12.0",
"license": "MIT"
},
+ "node_modules/select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+ },
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -2841,6 +2871,11 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
@@ -3340,6 +3375,17 @@
"typescript": "*"
}
},
+ "node_modules/vue3-json-viewer": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/vue3-json-viewer/-/vue3-json-viewer-2.2.2.tgz",
+ "integrity": "sha512-56l3XDGggnpwEqZieXsSMhNT4NhtO6d7zuSAxHo4i0UVxymyY2jRb7UMQOU1ztChKALZCAzX7DlgrsnEhxu77A==",
+ "dependencies": {
+ "clipboard": "^2.0.10"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
"node_modules/vue3-qrcode-reader": {
"version": "0.0.1",
"license": "MIT",
@@ -4164,6 +4210,16 @@
}
}
},
+ "clipboard": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+ "requires": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -4257,6 +4313,11 @@
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"dev": true
},
+ "delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -4758,6 +4819,14 @@
"path-is-absolute": "^1.0.0"
}
},
+ "good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+ "requires": {
+ "delegate": "^3.1.2"
+ }
+ },
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
@@ -5324,6 +5393,11 @@
"sdp": {
"version": "2.12.0"
},
+ "select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+ },
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -5476,6 +5550,11 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
@@ -5804,6 +5883,14 @@
"@volar/vue-typescript": "1.0.24"
}
},
+ "vue3-json-viewer": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/vue3-json-viewer/-/vue3-json-viewer-2.2.2.tgz",
+ "integrity": "sha512-56l3XDGggnpwEqZieXsSMhNT4NhtO6d7zuSAxHo4i0UVxymyY2jRb7UMQOU1ztChKALZCAzX7DlgrsnEhxu77A==",
+ "requires": {
+ "clipboard": "^2.0.10"
+ }
+ },
"vue3-qrcode-reader": {
"version": "0.0.1",
"requires": {
diff --git a/package.json b/package.json
index 1ec81c1ca..64814e79c 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@popperjs/core": "^2.11.2",
"@vitejs/plugin-vue": "^4.0.0",
"axios": "^0.26.0",
+ "clipboard": "^2.0.11",
"core-js": "^3.8.3",
"crypto-js": "^4.2.0",
"eslint-config-prettier": "^8.5.0",
@@ -27,6 +28,7 @@
"vite-plugin-vuetify": "^1.0.2",
"vue": "^3.2.47",
"vue-router": "^4.0.13",
+ "vue3-json-viewer": "^2.2.2",
"vue3-qrcode-reader": "^0.0.1",
"vuetify": "^3.1.4",
"vuex": "^4.0.2",
diff --git a/src/assets/MOCK/dpp.json b/src/assets/MOCK/dpp.json
index ef125a0bd..32ee224eb 100644
--- a/src/assets/MOCK/dpp.json
+++ b/src/assets/MOCK/dpp.json
@@ -1,5 +1,71 @@
{
- "metadata": {},
+ "metadata": {
+ "searchData": {
+ "contract": {
+ "@id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": {
+ "@id": "MDc4NjZjMTQtOGE0YS00ZjQ3LWEyYzgtMWI5MmEwOGQ1NTlm:dXJuOnV1aWQ6MGVjOGNmMmItZjU4ZS0zZjEzLWI1ZWYtZTdkZDAxZDE1YjE5:N2ZjNWNjM2UtMjhjZC00OWI5LTg3MjItZTQ5ODk2MDM1ZDBk",
+ "@type": "odrl:Set",
+ "odrl:permission": [
+ {
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e0000",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "ID 3.0 Trace"
+ }
+ }
+ },
+ {
+ "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e1111",
+ "odrl:action": {
+ "odrl:type": "ACCESS"
+ },
+ "odrl:constraint": {
+ "odrl:or": {
+ "odrl:leftOperand": "PURPOSE",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "DPP"
+ }
+ }
+ }
+ ],
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ },
+ "dcat:distribution": [
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
+ },
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
+ }
+ ],
+ "edc:description": "Digital Product Passport (DPP) test data",
+ "edc:id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
+ },
+ "id": "a4a816d9-86af-4abd-bffc-d1964698d39b",
+ "token": "5e2e1afd6bdca53de86368b9884b67b0755f4653c5b31cb5b6bc9f070a043672"
+ }
+ },
"semanticId": "general",
"aspect": {
"serialization": {
@@ -187,4 +253,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/assets/styles/components/general/contractModal.scss b/src/assets/styles/components/general/contractModal.scss
index 60c34f086..db8b5927a 100644
--- a/src/assets/styles/components/general/contractModal.scss
+++ b/src/assets/styles/components/general/contractModal.scss
@@ -41,4 +41,19 @@
font-weight: 600;
padding: 15px;
}
+ .details-btn {
+ margin: 20px;
+ color: $catena-x-blue;
+ text-decoration: underline;
+ }
+ .json-viewer-container {
+ border: 1px solid lightgray;
+ border-radius: 5px;
+ .json-viewer {
+ min-width: 300px;
+ max-height: 50vh;
+ overflow: auto;
+ background-color: rgb(245, 245, 245);
+ }
+ }
}
diff --git a/src/components/general/StepperItem.vue b/src/components/general/StepperItem.vue
index e036fa517..cc15a21c7 100644
--- a/src/components/general/StepperItem.vue
+++ b/src/components/general/StepperItem.vue
@@ -42,34 +42,22 @@
{{ condition ? idLabel : "" }}
-
+
Choose a policy
+ >
+
Agree
+
+
+ {{ detailsTitle }}
+
+
+
+
+
+
+
@@ -102,9 +107,14 @@
import { mapState } from "vuex";
import store from "../../store/index";
import BackendService from "@/services/BackendService";
-
+import { JsonViewer } from "vue3-json-viewer";
+import "vue3-json-viewer/dist/index.css";
+import { reactive } from "vue";
export default {
name: "StepperItem",
+ components: {
+ JsonViewer,
+ },
props: {
condition: { type: [String, Number], default: "" },
stepTitle: { type: [String, Number], default: "" },
@@ -116,8 +126,11 @@ export default {
},
data: () => ({
showOverlay: false,
- contractItems: [],
+ contractItems: reactive([]),
radios: 0,
+ details: false,
+ detailsTitle: "More details",
+ policies: [],
}),
computed: {
...mapState(["searchData", "contractToSign"]),
@@ -125,17 +138,55 @@ export default {
async created() {
this.backendService = new BackendService();
},
+
mounted() {
- this.contractItems =
- this.searchData.contract["odrl:hasPolicy"]["odrl:permission"];
- this.contractToSign = store.commit(
- "setContractToSign",
- this.contractItems[0]
- );
+ // Initialize contractItems from searchData
+ this.contractItems = this.searchData.contracts;
+
+ // Extract policies
+ this.extractPolicies(this.contractItems);
+
+ // Check if policies array has elements and then access the @id of the first element
+ if (this.policies.length > 0) {
+ const contractId = this.policies[0];
+
+ // Commit the contract ID to the store
+ this.$store.commit("setContractToSign", contractId);
+
+ // Store the ID in state
+ this.contractToSign = contractId;
+ } else {
+ console.error("No policies found");
+ }
this.shouldShowOverlay();
},
methods: {
+ extractPolicies(contracts) {
+ const policies = [];
+ contracts.forEach((contract) => {
+ if (contract["odrl:hasPolicy"]) {
+ // Check if 'odrl:hasPolicy' is an array
+ if (Array.isArray(contract["odrl:hasPolicy"])) {
+ contract["odrl:hasPolicy"].forEach((policy) => {
+ policies.push(policy);
+ });
+ } else {
+ // 'odrl:hasPolicy' is a single object
+ policies.push(contract["odrl:hasPolicy"]);
+ }
+ }
+ });
+ return (this.policies = policies);
+ },
+ toggleDetails() {
+ this.details = !this.details;
+ if (this.details) {
+ this.detailsTitle = "Less details";
+ } else {
+ this.detailsTitle = "More details";
+ }
+ },
operatorMapper(operator) {
let opr = operator.replace("odrl:", "");
if (opr == "eq") {
@@ -171,3 +222,4 @@ export default {
},
};
+
diff --git a/src/main.js b/src/main.js
index a7dbfde51..8827b41c6 100644
--- a/src/main.js
+++ b/src/main.js
@@ -28,13 +28,16 @@ import { loadFonts } from './assets/plugins/webfontloader';
import router from './router';
import '@/assets/styles/main.scss';
import authentication from '@/services/Authentication';
-
+import JsonViewer from "vue3-json-viewer";
+// if you used v1.0.5 or latster ,you should add import "vue3-json-viewer/dist/index.css"
+import "vue3-json-viewer/dist/index.css";
loadFonts();
const app = createApp(App);
app.use(vuetify);
app.use(store);
app.use(router);
+app.use(JsonViewer);
let auth = new authentication();
app.provide('authentication', auth);
diff --git a/src/store/index.js b/src/store/index.js
index b474f9c7c..9d4ffa3b8 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -56,65 +56,165 @@ export default createStore({
}
],
searchData: {
- "contract": {
- "@id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19",
- "@type": "dcat:Dataset",
- "odrl:hasPolicy": {
- "@id": "MDc4NjZjMTQtOGE0YS00ZjQ3LWEyYzgtMWI5MmEwOGQ1NTlm:dXJuOnV1aWQ6MGVjOGNmMmItZjU4ZS0zZjEzLWI1ZWYtZTdkZDAxZDE1YjE5:N2ZjNWNjM2UtMjhjZC00OWI5LTg3MjItZTQ5ODk2MDM1ZDBk",
- "@type": "odrl:Set",
- "odrl:permission": [{
- "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e0000",
- "odrl:action": {
- "odrl:type": "USE"
- },
- "odrl:constraint": {
- "odrl:or": {
- "odrl:leftOperand": "PURPOSE",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "ID 3.0 Trace"
+ "contracts": [
+ {
+ "@id": "9b3c0977-6b14-4201-bd76-55f681a92872",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": {
+ "@id": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
+ "@type": "odrl:Set",
+ "odrl:permission": {
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": [
+ {
+ "odrl:leftOperand": "Membership",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ },
+ {
+ "odrl:leftOperand": "FrameworkAgreement.sustainability",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ }
+ ]
}
- }
+ },
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
},
-
- {
- "odrl:target": "urn:uuid:d6a0ed29-8ba4-fc00-169c-72d3986e1111",
- "odrl:action": {
- "odrl:type": "ACCESS"
+ "dcat:distribution": [
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
+ },
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
+ }
+ ],
+ "edc:description": "Battery Passport test data",
+ "edc:id": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
+ },
+ {
+ "@id": "5c4fbb7d-cf02-4401-a7a3-f0ec1c506f33",
+ "@type": "dcat:Dataset",
+ "odrl:hasPolicy": [{
+ "@id": "2:1f4a64f0-aba9-498a-917c-4936c24c50cd-49a06ad2-64b7-46c8-9f3b-a718c462ca23:ac45d75a-2542-4d1a-a0fc-034c705418a9",
+ "@type": "odrl:Set",
+ "odrl:permission": {
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:or": [
+ {
+ "odrl:leftOperand": "Membership",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ },
+ {
+ "odrl:leftOperand": "FrameworkAgreement.sustainability",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ }
+ ]
+ }
},
- "odrl:constraint": {
- "odrl:or": {
- "odrl:leftOperand": "PURPOSE",
- "odrl:operator": {
- "@id": "odrl:eq"
+ "odrl:prohibition": [],
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
+ }, {
+ "@id": "2:67deb076-464c-4e8d-8931-087cee0afe2f:ac45d75a-2542-4d1a-a0fc-034c705418a9",
+ "@type": "odrl:Set",
+ "odrl:permission": {
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
+ "odrl:action": {
+ "odrl:type": "USE"
+ },
+ "odrl:constraint": {
+ "odrl:and": [
+ {
+ "odrl:leftOperand": "DPP",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": "active"
+ }
+ ]
+ }
+ },
+ "odrl:prohibition": {
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
+ "odrl:action": {
+ "odrl:type": "DISTRIBUTE"
+ },
+ "odrl:duties": [{
+ "odrl:action": {
+ "@id": "odrl:compensate"
},
- "odrl:rightOperand": "DPP"
+ "odrl:constraint": {
+ "odrl:name": "odrl:payAmount",
+ "odrl:operator": {
+ "@id": "odrl:eq"
+ },
+ "odrl:rightOperand": { "@value": "0.0", "@type": "xsd:decimal" },
+ "odrl:unit": "http://example.com/iso4217a/EUR"
+ }
+ }],
+ "odrl:constraint": {
+ "odrl:leftOperand": "event",
+ "odrl:operator": "odrl:lt",
+ "odrl:rightOperand": {
+ "@id": "odrl:policyUsage"
+ }
}
+ },
+ "odrl:obligation": [],
+ "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
+ }
+ ],
+ "dcat:distribution": [
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "HttpProxy"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
+ },
+ {
+ "@type": "dcat:Distribution",
+ "dct:format": {
+ "@id": "AmazonS3"
+ },
+ "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
}
- }],
- "odrl:prohibition": [],
- "odrl:obligation": [],
- "odrl:target": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
- },
- "dcat:distribution": [{
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "HttpProxy"
- },
- "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
- }, {
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "AmazonS3"
- },
- "dcat:accessService": "1bcbba32-d074-4bdc-b6fb-80f4e6202d3a"
- }],
- "edc:description": "Digital Product Passport (DPP) test data",
- "edc:id": "urn:uuid:0ec8cf2b-f58e-3f13-b5ef-e7dd01d15b19"
- },
- "id": "a4a816d9-86af-4abd-bffc-d1964698d39b",
- "token": "5e2e1afd6bdca53de86368b9884b67b0755f4653c5b31cb5b6bc9f070a043672"
+ ],
+ "edc:description": "Battery Passport test data",
+ "edc:id": "1f4a64f0-aba9-498a-917c-4936c24c50cd-49a06ad2-64b7-46c8-9f3b-a718c462ca23"
+ }
+ ],
+ "token": "688787d8ff144c502c7f5cffaafe2cc588d86079f9de88304c26b0cb99ce91c6",
+ "id": "ccbf6bfb-c7e1-4db4-8225-9fa95ee82f7f"
},
contractToSign: null,
processId: null,
From ee4e99a38be38ab582bd1451a5f24ddbb99e858e Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Mon, 29 Jan 2024 13:51:52 +0100
Subject: [PATCH 04/23] chore: runned IP check and approved vue3-json-viewer2
---
DEPENDENCIES_FRONTEND | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/DEPENDENCIES_FRONTEND b/DEPENDENCIES_FRONTEND
index 97d328fd1..f26ac5144 100644
--- a/DEPENDENCIES_FRONTEND
+++ b/DEPENDENCIES_FRONTEND
@@ -25,6 +25,7 @@ npm/npmjs/-/callsites/3.1.0, MIT, approved, clearlydefined
npm/npmjs/-/chalk/2.4.2, MIT, approved, clearlydefined
npm/npmjs/-/chalk/4.1.2, MIT, approved, clearlydefined
npm/npmjs/-/chokidar/3.5.3, MIT, approved, #2317
+npm/npmjs/-/clipboard/2.0.11, MIT, approved, clearlydefined
npm/npmjs/-/cliui/8.0.1, ISC AND Artistic-2.0, approved, #3753
npm/npmjs/-/color-convert/1.9.3, MIT, approved, clearlydefined
npm/npmjs/-/color-convert/2.0.1, MIT, approved, clearlydefined
@@ -92,6 +93,7 @@ npm/npmjs/-/get-caller-file/2.0.5, ISC, approved, clearlydefined
npm/npmjs/-/glob-parent/5.1.2, ISC, approved, clearlydefined
npm/npmjs/-/glob/7.2.3, ISC, approved, clearlydefined
npm/npmjs/-/globals/13.20.0, MIT, approved, #6953
+npm/npmjs/-/good-listener/1.2.2, MIT, approved, clearlydefined
npm/npmjs/-/graceful-fs/4.2.10, ISC, approved, #7413
npm/npmjs/-/has-flag/3.0.0, MIT, approved, clearlydefined
npm/npmjs/-/has-flag/4.0.0, MIT, approved, clearlydefined
@@ -179,6 +181,7 @@ npm/npmjs/-/rtcpeerconnection-shim/1.2.15, BSD-3-Clause, approved, clearlydefine
npm/npmjs/-/run-parallel/1.2.0, MIT, approved, clearlydefined
npm/npmjs/-/sass/1.58.0, Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND MIT AND MIT, approved, #6927
npm/npmjs/-/sdp/2.12.0, MIT, approved, #6956
+npm/npmjs/-/select/1.1.2, MIT, approved, clearlydefined
npm/npmjs/-/semver/7.5.4, ISC, approved, clearlydefined
npm/npmjs/-/shebang-command/2.0.0, MIT, approved, clearlydefined
npm/npmjs/-/shebang-regex/3.0.0, MIT, approved, clearlydefined
@@ -196,6 +199,7 @@ npm/npmjs/-/supports-color/5.5.0, MIT, approved, clearlydefined
npm/npmjs/-/supports-color/7.2.0, MIT, approved, clearlydefined
npm/npmjs/-/table/6.8.1, BSD-3-Clause, approved, #4596
npm/npmjs/-/text-table/0.2.0, MIT, approved, clearlydefined
+npm/npmjs/-/tiny-emitter/2.1.0, MIT, approved, clearlydefined
npm/npmjs/-/tiny-invariant/1.3.1, MIT, approved, clearlydefined
npm/npmjs/-/to-regex-range/5.0.1, MIT, approved, clearlydefined
npm/npmjs/-/type-check/0.4.0, MIT, approved, clearlydefined
@@ -223,6 +227,7 @@ npm/npmjs/-/vue-router/4.0.13, MIT, approved, clearlydefined
npm/npmjs/-/vue-template-compiler/2.7.14, 0BSD AND MIT AND MIT, approved, #3476
npm/npmjs/-/vue-tsc/1.0.24, MIT, approved, clearlydefined
npm/npmjs/-/vue/3.2.47, MIT, approved, #7094
+npm/npmjs/-/vue3-json-viewer/2.2.2, MIT AND ISC, approved, #12936
npm/npmjs/-/vue3-qrcode-reader/0.0.1, MIT AND (BSD-3-Clause AND MIT), approved, #6958
npm/npmjs/-/vuetify/3.1.4, MIT AND W3C-20150513, approved, #7092
npm/npmjs/-/vuex-persistedstate/4.1.0, MIT, approved, clearlydefined
From a79919ceb86ce23c32bd91131c321ab74be539e2 Mon Sep 17 00:00:00 2001
From: david zynda
Date: Wed, 31 Jan 2024 13:39:20 +0100
Subject: [PATCH 05/23] Confirmation + new model
---
package-lock.json | 11 +
.../components/general/contractModal.scss | 22 +-
src/components/general/StepperItem.vue | 230 ++++++++++++------
src/main.js | 1 -
src/store/index.js | 10 +-
src/views/PassportView.vue | 2 +-
6 files changed, 188 insertions(+), 88 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index f86565f70..af4cd964b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@popperjs/core": "^2.11.2",
"@vitejs/plugin-vue": "^4.0.0",
"axios": ">=1.6.0",
+ "clipboard": "^2.0.11",
"core-js": "^3.8.3",
"crypto-js": "^4.2.0",
"eslint-config-prettier": "^8.5.0",
@@ -1201,6 +1202,11 @@
"node": ">=0.4.0"
}
},
+ "node_modules/delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -4514,6 +4520,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
+ "delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
diff --git a/src/assets/styles/components/general/contractModal.scss b/src/assets/styles/components/general/contractModal.scss
index db8b5927a..cab271d4b 100644
--- a/src/assets/styles/components/general/contractModal.scss
+++ b/src/assets/styles/components/general/contractModal.scss
@@ -23,9 +23,17 @@
.contract-modal {
justify-content: center;
align-items: center;
+ .back-to-homepage {
+ margin: 5px 0;
+ }
.content-container {
margin-top: 20px;
- width: 300px;
+ width: 450px;
+
+ .policy-group-label {
+ margin-top: 15px;
+ font-weight: bolder;
+ }
}
.contract-container {
display: flex;
@@ -57,3 +65,15 @@
}
}
}
+
+@media (max-width: 628px) {
+ .contract-modal {
+ .content-container {
+ width: 320px;
+ display: inline;
+ .policy-group-label-mobile {
+ display: block;
+ }
+ }
+ }
+}
diff --git a/src/components/general/StepperItem.vue b/src/components/general/StepperItem.vue
index cc15a21c7..90236bc1a 100644
--- a/src/components/general/StepperItem.vue
+++ b/src/components/general/StepperItem.vue
@@ -44,56 +44,102 @@
-
- Choose a policy
-
-
-
-
-
- Decline
- Agree
-
-
-
- {{ detailsTitle }}
-
-
-
-
-
+
+
+ Choose a policy
+
+
+
+
+ Contract ID:
+ {{ contractId }}
+
+
+
+
+
+
+ Decline
+ Agree
+
+
+
+ {{ detailsTitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to decline?
-
-
+
+
+ This will take you back to the Homepage
+
+
+
+ Cancel
+ Yes, Decline
+
+
+
@@ -131,9 +177,21 @@ export default {
details: false,
detailsTitle: "More details",
policies: [],
+ declineContractModal: false,
+ showContractModal: true,
}),
computed: {
...mapState(["searchData", "contractToSign"]),
+ groupedPolicies() {
+ return this.policies.reduce((groups, policy) => {
+ const contractId = Object.keys(policy)[0];
+ if (!groups[contractId]) {
+ groups[contractId] = [];
+ }
+ groups[contractId].push(policy[contractId]);
+ return groups;
+ }, {});
+ },
},
async created() {
this.backendService = new BackendService();
@@ -148,13 +206,14 @@ export default {
// Check if policies array has elements and then access the @id of the first element
if (this.policies.length > 0) {
- const contractId = this.policies[0];
-
+ const firstPolicyObj = this.policies[0];
+ const initialContractToSign = Object.keys(firstPolicyObj)[0];
+ const initialPolicyToSign = firstPolicyObj[initialContractToSign]["@id"];
// Commit the contract ID to the store
- this.$store.commit("setContractToSign", contractId);
-
- // Store the ID in state
- this.contractToSign = contractId;
+ this.$store.commit("setContractToSign", {
+ contract: initialContractToSign,
+ policy: initialPolicyToSign,
+ });
} else {
console.error("No policies found");
}
@@ -163,21 +222,28 @@ export default {
},
methods: {
extractPolicies(contracts) {
- const policies = [];
- contracts.forEach((contract) => {
- if (contract["odrl:hasPolicy"]) {
- // Check if 'odrl:hasPolicy' is an array
+ let contractPolicies = [];
+
+ for (let key in contracts) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (contracts.hasOwnProperty(key)) {
+ const contract = contracts[key];
+
if (Array.isArray(contract["odrl:hasPolicy"])) {
contract["odrl:hasPolicy"].forEach((policy) => {
- policies.push(policy);
+ let policyEntry = {};
+ policyEntry[key] = policy;
+ contractPolicies.push(policyEntry);
});
} else {
- // 'odrl:hasPolicy' is a single object
- policies.push(contract["odrl:hasPolicy"]);
+ // Create an entry with the contract key and the policy object
+ let policyEntry = {};
+ policyEntry[key] = contract["odrl:hasPolicy"];
+ contractPolicies.push(policyEntry);
}
}
- });
- return (this.policies = policies);
+ }
+ return (this.policies = contractPolicies);
},
toggleDetails() {
this.details = !this.details;
@@ -187,29 +253,25 @@ export default {
this.detailsTitle = "More details";
}
},
- operatorMapper(operator) {
- let opr = operator.replace("odrl:", "");
- if (opr == "eq") {
- return " = ";
- }
- return opr;
- },
- chooseContract(contract) {
- return (this.contractToSign = store.commit(
- "setContractToSign",
- contract
- ));
+ chooseContract(contract, policy) {
+ console.log("Contract chosen - contractToSign:", this.contractToSign);
+ console.log("Contract chosen - policies:", this.policies);
+
+ return (this.contractToSign = store.commit("setContractToSign", {
+ contract: contract,
+ policy: policy,
+ }));
},
shouldShowOverlay() {
- if (this.contractItems.length > 1) {
+ if (this.policies.length > 1) {
return (this.showOverlay = true);
}
},
- async callAcceptContract(contractToSign) {
+ callAcceptContract(contract, policy) {
+ alert("contract: " + contract + " " + "policy: " + policy);
try {
- // let response = await this.backendService.acceptContract(contractToSign);
+ // let response = await this.backendService.acceptContract(contract, policy);
// return response;
- alert(contractToSign);
} catch (error) {
console.error("Error accepting contract", error);
} finally {
@@ -217,8 +279,16 @@ export default {
}
},
declineContract() {
+ this.declineContractModal = true;
+ this.showContractModal = false;
+ },
+ confirmDeclineContract() {
this.$router.push("/");
},
+ cancelDeclineContract() {
+ this.declineContractModal = false;
+ this.showContractModal = true;
+ },
},
};
diff --git a/src/main.js b/src/main.js
index 449b4ea2b..99fe60907 100644
--- a/src/main.js
+++ b/src/main.js
@@ -31,7 +31,6 @@ import authentication from '@/services/Authentication';
import JsonViewer from "vue3-json-viewer";
import "vue3-json-viewer/dist/index.css";
import { createI18n } from 'vue-i18n';
-// Import translation files
import en from '@/translations/en.json';
import de from '@/translations/de.json';
diff --git a/src/store/index.js b/src/store/index.js
index 5cb9cec41..7d6d7c4ea 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -56,8 +56,8 @@ export default createStore({
}
],
searchData: {
- "contracts": [
- {
+ "contracts": {
+ "9b3c0977-6b14-4201-bd76-55f681a92872": {
"@id": "9b3c0977-6b14-4201-bd76-55f681a92872",
"@type": "dcat:Dataset",
"odrl:hasPolicy": {
@@ -110,7 +110,7 @@ export default createStore({
"edc:description": "Battery Passport test data",
"edc:id": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
},
- {
+ "5c4fbb7d-cf02-4401-a7a3-f0ec1c506f33": {
"@id": "5c4fbb7d-cf02-4401-a7a3-f0ec1c506f33",
"@type": "dcat:Dataset",
"odrl:hasPolicy": [{
@@ -212,11 +212,11 @@ export default createStore({
"edc:description": "Battery Passport test data",
"edc:id": "1f4a64f0-aba9-498a-917c-4936c24c50cd-49a06ad2-64b7-46c8-9f3b-a718c462ca23"
}
- ],
+ },
"token": "688787d8ff144c502c7f5cffaafe2cc588d86079f9de88304c26b0cb99ce91c6",
"id": "ccbf6bfb-c7e1-4db4-8225-9fa95ee82f7f"
},
- contractToSign: null,
+ contractToSign: {},
processId: null,
searchContractId: null,
irsState: false,
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index b630b9369..e92d749c2 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -206,7 +206,7 @@ export default {
irsData: [],
processId: null,
backendService: null,
- error: false,
+ error: true,
errorObj: {
title: "Something went wrong while returning the passport!",
description: "We are sorry for that, you can retry or try again later",
From ea216db54ad0fda0d97928cc108e70c3a17ce082 Mon Sep 17 00:00:00 2001
From: david zynda
Date: Wed, 31 Jan 2024 20:52:25 +0100
Subject: [PATCH 06/23] contract sign moved to PassportView
---
src/components/general/LoadingComponent.vue | 1 -
src/components/general/StepperItem.vue | 230 --------------------
src/store/index.js | 173 +--------------
src/views/PassportView.vue | 223 ++++++++++++++++++-
4 files changed, 214 insertions(+), 413 deletions(-)
diff --git a/src/components/general/LoadingComponent.vue b/src/components/general/LoadingComponent.vue
index 12ddb2adb..b72b51b8e 100644
--- a/src/components/general/LoadingComponent.vue
+++ b/src/components/general/LoadingComponent.vue
@@ -91,7 +91,6 @@
$t(this.stepsNames.contractNegotiation.initialStepSubtitle)
"
displayId
- contractSign
:idLabel="
statusData.data.history['negotiation-accepted']
? statusData.data.history['negotiation-accepted'].status
diff --git a/src/components/general/StepperItem.vue b/src/components/general/StepperItem.vue
index 90236bc1a..b216eacde 100644
--- a/src/components/general/StepperItem.vue
+++ b/src/components/general/StepperItem.vue
@@ -42,106 +42,6 @@
{{ condition ? idLabel : "" }}
-
-
-
-
- Choose a policy
-
-
-
-
- Contract ID:
- {{ contractId }}
-
-
-
-
-
-
- Decline
- Agree
-
-
-
- {{ detailsTitle }}
-
-
-
-
-
-
-
-
-
-
-
-
- Are you sure you want to decline?
-
-
-
- This will take you back to the Homepage
-
-
-
- Cancel
- Yes, Decline
-
-
-
-
-
@@ -150,146 +50,16 @@
diff --git a/src/store/index.js b/src/store/index.js
index 7d6d7c4ea..51d237ddd 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -39,182 +39,11 @@ export default createStore({
}
},
irsData: [
- {
- "id": "urn:uuid:efcb5f8d-f31c-4b1f-b090-9c878054554d",
- "name": "Battery_BAT-XYZ789",
- "searchId": "CX:XYZ78901:BAT-XYZ789",
- "path": "/urn:uuid:efcb5f8d-f31c-4b1f-b090-9c878054554d",
- "children": [
- {
- "id": "urn:uuid:d8ec6acc-1ad7-47b4-bc7e-612122d9d552",
- "name": "BatteryModule_EVMODULE-TRJ712",
- "searchId": "CX:XYZ78901:EVMODULE-TRJ712",
- "path": "/urn:uuid:efcb5f8d-f31c-4b1f-b090-9c878054554d/urn:uuid:d8ec6acc-1ad7-47b4-bc7e-612122d9d552",
- "children": []
- }
- ]
- }
+ {}
],
searchData: {
"contracts": {
- "9b3c0977-6b14-4201-bd76-55f681a92872": {
- "@id": "9b3c0977-6b14-4201-bd76-55f681a92872",
- "@type": "dcat:Dataset",
- "odrl:hasPolicy": {
- "@id": "3:365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918:dc616f20-2781-450a-837a-290d861c8e0a",
- "@type": "odrl:Set",
- "odrl:permission": {
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
- "odrl:action": {
- "odrl:type": "USE"
- },
- "odrl:constraint": {
- "odrl:or": [
- {
- "odrl:leftOperand": "Membership",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "active"
- },
- {
- "odrl:leftOperand": "FrameworkAgreement.sustainability",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "active"
- }
- ]
- }
- },
- "odrl:prohibition": [],
- "odrl:obligation": [],
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
- },
- "dcat:distribution": [
- {
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "HttpProxy"
- },
- "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
- },
- {
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "AmazonS3"
- },
- "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
- }
- ],
- "edc:description": "Battery Passport test data",
- "edc:id": "365e6fbe-bb34-11ec-8422-0242ac120002-61125dc3-5e6f-4f4b-838d-447432b97918"
- },
- "5c4fbb7d-cf02-4401-a7a3-f0ec1c506f33": {
- "@id": "5c4fbb7d-cf02-4401-a7a3-f0ec1c506f33",
- "@type": "dcat:Dataset",
- "odrl:hasPolicy": [{
- "@id": "2:1f4a64f0-aba9-498a-917c-4936c24c50cd-49a06ad2-64b7-46c8-9f3b-a718c462ca23:ac45d75a-2542-4d1a-a0fc-034c705418a9",
- "@type": "odrl:Set",
- "odrl:permission": {
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
- "odrl:action": {
- "odrl:type": "USE"
- },
- "odrl:constraint": {
- "odrl:or": [
- {
- "odrl:leftOperand": "Membership",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "active"
- },
- {
- "odrl:leftOperand": "FrameworkAgreement.sustainability",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "active"
- }
- ]
- }
- },
- "odrl:prohibition": [],
- "odrl:obligation": [],
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
- }, {
- "@id": "2:67deb076-464c-4e8d-8931-087cee0afe2f:ac45d75a-2542-4d1a-a0fc-034c705418a9",
- "@type": "odrl:Set",
- "odrl:permission": {
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
- "odrl:action": {
- "odrl:type": "USE"
- },
- "odrl:constraint": {
- "odrl:and": [
- {
- "odrl:leftOperand": "DPP",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": "active"
- }
- ]
- }
- },
- "odrl:prohibition": {
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27",
- "odrl:action": {
- "odrl:type": "DISTRIBUTE"
- },
- "odrl:duties": [{
- "odrl:action": {
- "@id": "odrl:compensate"
- },
- "odrl:constraint": {
- "odrl:name": "odrl:payAmount",
- "odrl:operator": {
- "@id": "odrl:eq"
- },
- "odrl:rightOperand": { "@value": "0.0", "@type": "xsd:decimal" },
- "odrl:unit": "http://example.com/iso4217a/EUR"
- }
- }],
- "odrl:constraint": {
- "odrl:leftOperand": "event",
- "odrl:operator": "odrl:lt",
- "odrl:rightOperand": {
- "@id": "odrl:policyUsage"
- }
- }
- },
- "odrl:obligation": [],
- "odrl:target": "urn:uuid:748cf682-6747-33cb-630b-c35a29970f27"
- }
- ],
- "dcat:distribution": [
- {
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "HttpProxy"
- },
- "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
- },
- {
- "@type": "dcat:Distribution",
- "dct:format": {
- "@id": "AmazonS3"
- },
- "dcat:accessService": "1795254a-e354-46c7-9d88-04608b05ca9f"
- }
- ],
- "edc:description": "Battery Passport test data",
- "edc:id": "1f4a64f0-aba9-498a-917c-4936c24c50cd-49a06ad2-64b7-46c8-9f3b-a718c462ca23"
- }
},
- "token": "688787d8ff144c502c7f5cffaafe2cc588d86079f9de88304c26b0cb99ce91c6",
- "id": "ccbf6bfb-c7e1-4db4-8225-9fa95ee82f7f"
},
contractToSign: {},
processId: null,
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index b0aa91bee..e68e0944b 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -48,6 +48,99 @@
+
+
+
+
+ Choose a policy
+
+
+
+
+ Contract ID:
+ {{ contractId }}
+
+
+
+
+
+
+ Decline
+ Agree
+
+
+
+ {{ detailsTitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to decline?
+
+
+
+ This will take you back to the Homepage
+
+
+
+ Cancel
+ Yes, Decline
+
+
+
+
+
@@ -133,17 +226,27 @@ import TransmissionCards from "@/components/passport/TransmissionCards.vue";
import GeneralCards from "@/components/passport/GeneralCards.vue";
import FooterComponent from "@/components/general/Footer.vue";
import ErrorComponent from "@/components/general/ErrorComponent.vue";
-import { AUTO_SIGN, SEARCH_TIMEOUT, NEGOTIATE_TIMEOUT } from "@/services/service.const";
+import {
+ AUTO_SIGN,
+ SEARCH_TIMEOUT,
+ NEGOTIATE_TIMEOUT,
+} from "@/services/service.const";
import threadUtil from "@/utils/threadUtil.js";
import jsonUtil from "@/utils/jsonUtil.js";
import configUtil from "@/utils/configUtil.js";
import passportUtil from "@/utils/passportUtil.js";
import BackendService from "@/services/BackendService";
import { inject } from "vue";
+import { mapState } from "vuex";
+import store from "../store/index";
+import { JsonViewer } from "vue3-json-viewer";
+import "vue3-json-viewer/dist/index.css";
+import { reactive } from "vue";
export default {
name: "PassportView",
components: {
+ JsonViewer,
HeaderComponent,
FooterComponent,
PassportHeader,
@@ -156,6 +259,14 @@ export default {
},
data() {
return {
+ showOverlay: false,
+ contractItems: reactive([]),
+ radios: 0,
+ details: false,
+ detailsTitle: "More details",
+ policies: [],
+ declineContractModal: false,
+ showContractModal: true,
batteryComponentsNames: [
{
label: "passportView.batteryComponentsNames.generalInformation",
@@ -215,11 +326,33 @@ export default {
type: "error",
status: 500,
statusText: "Internal Server Error",
- reload: true
+ reload: true,
},
};
},
+ mounted() {
+ // Initialize contractItems from searchData
+ this.contractItems = this.searchData.contracts;
+
+ // Extract policies
+ this.extractPolicies(this.contractItems);
+
+ // Check if policies array has elements and then access the @id of the first element
+ if (this.policies.length > 0) {
+ const firstPolicyObj = this.policies[0];
+ const initialContractToSign = Object.keys(firstPolicyObj)[0];
+ const initialPolicyToSign = firstPolicyObj[initialContractToSign]["@id"];
+ // Commit the contract ID to the store
+ this.$store.commit("setContractToSign", {
+ contract: initialContractToSign,
+ policy: initialPolicyToSign,
+ });
+ } else {
+ console.error("No policies found");
+ }
+ this.shouldShowOverlay();
+ },
computed: {
filteredComponentsNames() {
let dataKeys = Object.keys(this.data.aspect);
@@ -238,7 +371,18 @@ export default {
} else {
return [];
}
- }
+ },
+ ...mapState(["searchData", "contractToSign"]),
+ groupedPolicies() {
+ return this.policies.reduce((groups, policy) => {
+ const contractId = Object.keys(policy)[0];
+ if (!groups[contractId]) {
+ groups[contractId] = [];
+ }
+ groups[contractId].push(policy[contractId]);
+ return groups;
+ }, {});
+ },
},
async created() {
@@ -246,6 +390,63 @@ export default {
this.searchContracts();
},
methods: {
+ extractPolicies(contracts) {
+ let contractPolicies = [];
+
+ for (let key in contracts) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (contracts.hasOwnProperty(key)) {
+ const contract = contracts[key];
+
+ if (Array.isArray(contract["odrl:hasPolicy"])) {
+ contract["odrl:hasPolicy"].forEach((policy) => {
+ let policyEntry = {};
+ policyEntry[key] = policy;
+ contractPolicies.push(policyEntry);
+ });
+ } else {
+ // Create an entry with the contract key and the policy object
+ let policyEntry = {};
+ policyEntry[key] = contract["odrl:hasPolicy"];
+ contractPolicies.push(policyEntry);
+ }
+ }
+ }
+ return (this.policies = contractPolicies);
+ },
+ toggleDetails() {
+ this.details = !this.details;
+ if (this.details) {
+ this.detailsTitle = "Less details";
+ } else {
+ this.detailsTitle = "More details";
+ }
+ },
+ chooseContract(contract, policy) {
+ console.log("Contract chosen - contractToSign:", this.contractToSign);
+ console.log("Contract chosen - policies:", this.policies);
+
+ return (this.contractToSign = store.commit("setContractToSign", {
+ contract: contract,
+ policy: policy,
+ }));
+ },
+ shouldShowOverlay() {
+ if (this.policies.length > 1) {
+ return (this.showOverlay = true);
+ }
+ },
+ declineContract() {
+ this.declineContractModal = true;
+ this.showContractModal = false;
+ },
+ confirmDeclineContract() {
+ this.$router.push("/");
+ },
+ cancelDeclineContract() {
+ this.declineContractModal = false;
+ this.showContractModal = true;
+ },
async searchContracts() {
let result = null;
try {
@@ -258,7 +459,8 @@ export default {
null
);
if (!result || result == null) {
- this.errorObj.title = "Timeout! Failed to search for the Digital Twin Registry and the Digital Twin!";
+ this.errorObj.title =
+ "Timeout! Failed to search for the Digital Twin Registry and the Digital Twin!";
this.errorObj.description =
"The request took too long... Please retry or try again later.";
this.status = 408;
@@ -268,7 +470,6 @@ export default {
} catch (e) {
console.log("searchContracts -> " + e);
} finally {
-
console.log(this.searchResponse);
console.log(this.searchResponse["data"]);
if (
@@ -283,11 +484,11 @@ export default {
this.error = false;
console.log(this.searchResponse);
console.log("AutoSign -> " + AUTO_SIGN);
- if(AUTO_SIGN){
+ if (AUTO_SIGN) {
this.resumeNegotiation(this.searchResponse);
}
}
- if(this.error || !AUTO_SIGN){
+ if (this.error || !AUTO_SIGN) {
// Stop loading
this.loading = false;
}
@@ -295,14 +496,15 @@ export default {
},
async resumeNegotiation(
searchResponse,
- contractId = null,
- policyId = null
+ contractId = this.contractToSign.contract,
+ policyId = this.contractToSign.policy
) {
let result = null;
let contracts = jsonUtil.get("data.contracts", searchResponse);
let token = jsonUtil.get("data.token", searchResponse);
- let processId = jsonUtil.get("data.id", searchResponse);
+ let processId = jsonUtil.get("data.id", searchResponse);
// [TODO] Get Contract Information
+ console.log("resume negotiation:" + contractId + policyId);
try {
// Setup aspect promise
let passportPromise = this.negotiatePassport(
@@ -358,6 +560,7 @@ export default {
}
// Stop loading
this.loading = false;
+ this.contractItems = [];
}
},
async searchAsset(id) {
From d0c243c26af14f32e8735578fd737bc41fbe289f Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Thu, 1 Feb 2024 16:31:30 +0100
Subject: [PATCH 07/23] chore: fixed integrity of policy sign component
---
.../templates/deployment-frontend.yaml | 7 +-
charts/digital-product-pass/values.yaml | 7 +-
entrypoint.sh | 1 +
package-lock.json | 24 --
src/services/service.const.js | 8 +-
src/views/PassportView.vue | 352 ++++++++++++------
6 files changed, 251 insertions(+), 148 deletions(-)
diff --git a/charts/digital-product-pass/templates/deployment-frontend.yaml b/charts/digital-product-pass/templates/deployment-frontend.yaml
index 9163fc9bf..893746da0 100644
--- a/charts/digital-product-pass/templates/deployment-frontend.yaml
+++ b/charts/digital-product-pass/templates/deployment-frontend.yaml
@@ -94,10 +94,13 @@ spec:
value: "https://{{ .Values.oauth.hostname }}"
- name: "API_NEGOTIATE_TIMEOUT"
- value: "{{ .Values.frontend.api.negotiateTimeout }}"
+ value: "{{ .Values.frontend.api.timeout.negotiate }}"
- name: "API_SEARCH_TIMEOUT"
- value: "{{ .Values.frontend.api.searchTimeout }}"
+ value: "{{ .Values.frontend.api.timeout.search }}"
+
+ - name: "API_DECLINE_TIMEOUT"
+ value: "{{ .Values.frontend.api.timeout.decline }}"
- name: "API_MAX_RETRIES"
value: "{{ .Values.frontend.api.max_retries }}"
diff --git a/charts/digital-product-pass/values.yaml b/charts/digital-product-pass/values.yaml
index a2e83b8fb..6b57824ba 100644
--- a/charts/digital-product-pass/values.yaml
+++ b/charts/digital-product-pass/values.yaml
@@ -171,9 +171,10 @@ frontend:
# -- max retries for getting status
max_retries: 30
# -- default timeout - 90 seconds in milliseconds
- negotiateTimeout: 20000
- searchTimeout: 40000
-
+ timeout:
+ negotiate: 20000
+ search: 40000
+ decline: 20000
# -- delay from getting status
delay: 1000
diff --git a/entrypoint.sh b/entrypoint.sh
index 89cc5fe88..d5423399b 100644
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -33,6 +33,7 @@ do
sed -i 's|APP_VERSION|'${VERSION}'|g' $file
sed -i 's|API_SEARCH_TIMEOUT|'${API_SEARCH_TIMEOUT}'|g' $file
sed -i 's|API_NEGOTIATE_TIMEOUT|'${API_NEGOTIATE_TIMEOUT}'|g' $file
+ sed -i 's|API_DECLINE_TIMEOUT|'${API_DECLINE_TIMEOUT}'|g' $file
sed -i 's|APP_API_DELAY|'${API_DELAY}'|g' $file
sed -i 's|APP_API_MAX_RETRIES|'${API_MAX_RETRIES}'|g' $file
sed -i 's|KEYCLOAK_CLIENTID|'${KEYCLOAK_CLIENTID}'|g' $file
diff --git a/package-lock.json b/package-lock.json
index 8defb0bea..7dfba514d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1996,12 +1996,6 @@
"delegate": "^3.1.2"
}
},
- "node_modules/graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
- },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -2981,12 +2975,6 @@
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
- "node_modules/tiny-invariant": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
- "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
- "dev": true
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4805,12 +4793,6 @@
"delegate": "^3.1.2"
}
},
- "graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
- },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -5497,12 +5479,6 @@
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
- "tiny-invariant": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
- "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
- "dev": true
- },
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
diff --git a/src/services/service.const.js b/src/services/service.const.js
index d3cec26a9..61da7f440 100644
--- a/src/services/service.const.js
+++ b/src/services/service.const.js
@@ -35,6 +35,7 @@ let backendUrl = "DATA_URL";
let retries = 'APP_API_MAX_RETRIES';
let searchTimeout = 'API_SEARCH_TIMEOUT';
let negotiateTimeout = 'API_NEGOTIATE_TIMEOUT';
+let declineTimeout = 'API_DECLINE_TIMEOUT';
let irsDelay = 'APP_IRS_DELAY';
let irsMaxWaitingTime = 'APP_IRS_WAITING_TIME';
let delay = 'APP_API_DELAY';
@@ -50,6 +51,7 @@ let portalUrl = "APP_PORTAL_URL";
let adminEmail = "APP_ADMIN_EMAIL";
let autoSign = "APP_AUTO_SIGN";
+
// Default values if the value is not specified
serverUrl = (serverUrl != null && serverUrl !== "") ? serverUrl : "https://materialpass.int.demo.catena-x.net"
backendUrl = (backendUrl != null && backendUrl !== "") ? backendUrl : serverUrl
@@ -64,7 +66,8 @@ autoSign = (autoSign === "true")
// Default Variables if value is not specified or is not a integer
searchTimeout = numberUtil.parseInt(searchTimeout, 40000);
-negotiateTimeout = numberUtil.parseInt(searchTimeout, 20000);
+negotiateTimeout = numberUtil.parseInt(negotiateTimeout, 20000);
+declineTimeout = numberUtil.parseInt(declineTimeout, 20000);
delay = numberUtil.parseInt(delay, 1000);
retries = numberUtil.parseInt(retries, 20);
irsDelay = numberUtil.parseInt(irsDelay, 30000);
@@ -76,6 +79,7 @@ const BACKEND_URL = backendUrl;
const API_MAX_RETRIES = retries;
const NEGOTIATE_TIMEOUT = negotiateTimeout;
const SEARCH_TIMEOUT = searchTimeout;
+const DECLINE_TIMEOUT = declineTimeout;
const IRS_DELAY = irsDelay;
const IRS_MAX_WAITING_TIME = irsMaxWaitingTime;
const API_DELAY = delay;
@@ -111,4 +115,4 @@ if (window.location.href.includes("localhost")) { //Modify credentials for local
REDIRECT_URI = SERVER_URL;
}
// Export all the CONSTANTS and VARIABLES
-export { INIT_OPTIONS, REDIRECT_URI, SERVER_URL, IDP_URL, BACKEND_URL, VERSION, NEGOTIATE_TIMEOUT, SEARCH_TIMEOUT, API_DELAY, API_MAX_RETRIES, COMMIT_ID, REPO_ENDPOINT,IRS_DELAY, IRS_MAX_WAITING_TIME, BPN_CHECK, BPN, PORTAL_URL, ADMIN_EMAIL, ROLE_CHECK, AUTO_SIGN};
+export { INIT_OPTIONS, REDIRECT_URI, SERVER_URL, IDP_URL, BACKEND_URL, VERSION, NEGOTIATE_TIMEOUT, DECLINE_TIMEOUT, SEARCH_TIMEOUT, API_DELAY, API_MAX_RETRIES, COMMIT_ID, REPO_ENDPOINT,IRS_DELAY, IRS_MAX_WAITING_TIME, BPN_CHECK, BPN, PORTAL_URL, ADMIN_EMAIL, ROLE_CHECK, AUTO_SIGN};
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 7ae6c83e6..4d3aa253f 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -48,97 +48,111 @@
-
-
-
-
- Choose a policy
-
-
-
-
- Contract ID:
- {{ contractId }}
-
-
-
-
-
-
- Decline
- Agree
-
-
-
- {{ detailsTitle }}
-
-
-
-
-
+
+
+
+
+
+ Choose a policy
+
+
+
+
+ Contract ID:
+ {{ contractId }}
-
-
-
-
-
-
- Are you sure you want to decline?
+
+
+
+
+
+ Decline
+ Agree
+
+
+
+ {{ detailsTitle }}
+
+
+
+
+
-
-
- This will take you back to the Homepage
-
+
+
+
+
+
+
+
+ Are you sure you want to decline?
+
+
+ This will take you back to the Homepage
-
- Cancel
- Yes, Decline
-
-
-
+
+
+ Cancel
+ Yes, Decline
+
+
@@ -230,6 +244,7 @@ import {
AUTO_SIGN,
SEARCH_TIMEOUT,
NEGOTIATE_TIMEOUT,
+ DECLINE_TIMEOUT,
} from "@/services/service.const";
import threadUtil from "@/utils/threadUtil.js";
import jsonUtil from "@/utils/jsonUtil.js";
@@ -314,6 +329,7 @@ export default {
data: null,
loading: true,
searchResponse: null,
+ declineLoading: false,
errors: [],
id: this.$route.params.id,
irsData: [],
@@ -330,29 +346,6 @@ export default {
},
};
},
- mounted() {
- // Initialize contractItems from searchData
- this.contractItems = this.searchData.contracts;
-
- // Extract policies
- this.extractPolicies(this.contractItems);
-
- // Check if policies array has elements and then access the @id of the first element
- if (this.policies.length > 0) {
- const firstPolicyObj = this.policies[0];
- const initialContractToSign = Object.keys(firstPolicyObj)[0];
- const initialPolicyToSign = firstPolicyObj[initialContractToSign]["@id"];
- // Commit the contract ID to the store
- this.$store.commit("setContractToSign", {
- contract: initialContractToSign,
- policy: initialPolicyToSign,
- });
- } else {
- console.error("No policies found");
- }
-
- this.shouldShowOverlay();
- },
computed: {
filteredComponentsNames() {
let dataKeys = Object.keys(this.data.aspect);
@@ -432,7 +425,7 @@ export default {
}));
},
shouldShowOverlay() {
- if (this.policies.length > 1) {
+ if (this.policies.length > 0) {
return (this.showOverlay = true);
}
},
@@ -441,7 +434,11 @@ export default {
this.showContractModal = false;
},
confirmDeclineContract() {
- this.$router.push("/");
+ this.declineLoading = true;
+ this.triggerDecline(this.searchResponse);
+ if (!this.error) {
+ this.$router.push("/");
+ }
},
cancelDeclineContract() {
this.declineContractModal = false;
@@ -480,8 +477,30 @@ export default {
jsonUtil.exists("id", this.searchResponse["data"])
) {
this.error = false;
- if(AUTO_SIGN){
+ if (AUTO_SIGN) {
this.resumeNegotiation(this.searchResponse);
+ } else {
+ // Initialize contractItems from searchData
+ this.contractItems = jsonUtil.get(
+ "data.contracts",
+ this.searchResponse
+ );
+
+ // Extract policies
+ this.extractPolicies(this.contractItems);
+
+ // Check if policies array has elements and then access the @id of the first element
+ const firstPolicyObj = this.policies[0];
+ const initialContractToSign = Object.keys(firstPolicyObj)[0];
+ const initialPolicyToSign =
+ firstPolicyObj[initialContractToSign]["@id"];
+ // Commit the contract ID to the store
+ this.$store.commit("setContractToSign", {
+ contract: initialContractToSign,
+ policy: initialPolicyToSign,
+ });
+
+ this.shouldShowOverlay();
}
}
if (this.error || !AUTO_SIGN) {
@@ -490,11 +509,49 @@ export default {
}
}
},
+ async triggerDecline(searchResponse) {
+ let result = null;
+ let token = jsonUtil.get("data.token", searchResponse);
+ let processId = jsonUtil.get("data.id", searchResponse);
+ try {
+ // Setup aspect promise
+ let passportPromise = this.declineNegotiation(token, processId);
+ // Execute promisse with a Timeout
+ result = await threadUtil.execWithTimeout(
+ passportPromise,
+ DECLINE_TIMEOUT,
+ null
+ );
+ if (!result || result == null) {
+ this.errorObj.title = "Timeout! Failed to decline negotiation!";
+ this.errorObj.description =
+ "The request took too long... Please retry or try again later.";
+ this.status = 408;
+ this.statusText = "Request Timeout";
+ }
+ this.data = result;
+ } catch (e) {
+ console.log("passportView -> " + e);
+ } finally {
+ if (
+ this.data &&
+ jsonUtil.exists("status", this.data) &&
+ this.data["status"] == 200
+ ) {
+ this.error = false;
+ }
+ // Stop loading
+ this.loading = false;
+ this.declineLoading = false;
+ }
+ },
async resumeNegotiation(
searchResponse,
- contractId = this.contractToSign.contract,
- policyId = this.contractToSign.policy
+ contractId = null,
+ policyId = null
) {
+ this.loading = true;
+ this.showOverlay = false;
let result = null;
let contracts = jsonUtil.get("data.contracts", searchResponse);
let token = jsonUtil.get("data.token", searchResponse);
@@ -528,7 +585,7 @@ export default {
} catch (e) {
console.log("passportView -> " + e);
} finally {
- console.log(this.data)
+ console.log(this.data);
if (
this.data &&
jsonUtil.exists("status", this.data) &&
@@ -684,6 +741,67 @@ export default {
: "Not found";
}
+ return response;
+ },
+ async declineNegotiation(token, processId) {
+ let response = null;
+ // Get Passport in Backend
+ try {
+ // Init backendService
+ // Get access token from IDP
+ // Get the aspect for the selected version
+
+ response = await this.backendService.declineNegotiation(
+ token,
+ processId,
+ this.auth
+ );
+ } catch (e) {
+ console.log("passportView.declineNegotiation() -> " + e);
+ this.errorObj.title = jsonUtil.exists("message", response)
+ ? response["message"]
+ : "Failed to return passport";
+ this.errorObj.description =
+ "It was not possible to transfer the passport.";
+
+ this.errorObj.status = jsonUtil.exists("status", response)
+ ? response["status"]
+ : 500;
+
+ this.errorObj.statusText = jsonUtil.exists("statusText", response)
+ ? response["statusText"]
+ : "Internal Server Error";
+ return response;
+ }
+
+ // response = jsonUtil.copy(response, true);
+
+ // Check if the response is empty and give an error
+ if (!response) {
+ this.errorObj.title = "Failed to return passport";
+ this.errorObj.description =
+ "It was not possible to complete the passport transfer.";
+ this.errorObj.status = 400;
+ this.errorObj.statusText = "Bad Request";
+ return null;
+ }
+
+ // Check if reponse content was successfull and if not print error comming message from backend
+ if (jsonUtil.exists("status", response) && response["status"] != 200) {
+ this.errorObj.title = jsonUtil.exists("message", response)
+ ? response["message"]
+ : "An error occured when searching for the passport!";
+ this.errorObj.description =
+ "An error occured when searching for the passport!";
+ this.errorObj.status = jsonUtil.exists("status", response)
+ ? response["status"]
+ : 404;
+
+ this.errorObj.statusText = jsonUtil.exists("statusText", response)
+ ? response["statusText"]
+ : "Not found";
+ }
+
return response;
},
},
From 8981aa02e4a7047686bb2274092b8c828183e8a3 Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Thu, 1 Feb 2024 18:19:18 +0100
Subject: [PATCH 08/23] feat: removed overlay for policy selection
---
.../components/general/contractModal.scss | 6 +-
src/views/PassportView.vue | 156 +++++++++---------
2 files changed, 80 insertions(+), 82 deletions(-)
diff --git a/src/assets/styles/components/general/contractModal.scss b/src/assets/styles/components/general/contractModal.scss
index cab271d4b..78cada18f 100644
--- a/src/assets/styles/components/general/contractModal.scss
+++ b/src/assets/styles/components/general/contractModal.scss
@@ -28,7 +28,7 @@
}
.content-container {
margin-top: 20px;
- width: 450px;
+ width: 500px;
.policy-group-label {
margin-top: 15px;
@@ -42,10 +42,9 @@
align-items: center !important;
padding: 20px 80px 50px 80px;
}
-
.title-container {
width: fit-content;
- font-size: 20px;
+ font-size: 1.5em;
font-weight: 600;
padding: 15px;
}
@@ -57,6 +56,7 @@
.json-viewer-container {
border: 1px solid lightgray;
border-radius: 5px;
+ word-break: break-word;
.json-viewer {
min-width: 300px;
max-height: 50vh;
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 4d3aa253f..8d5a13eae 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -51,11 +51,12 @@
-
-
- Choose a policy
+
+
+ Choose a policy:
+
- Decline
- Agree
-
-
-
- {{ detailsTitle }}
-
-
-
-
-
-
-
-
+
+ Decline
+ Agree
+
+
+
+ {{ detailsTitle }}
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to decline?
+
+
+ This will take you back to the Homepage
+
+
+
+ Cancel
+ Yes, Decline
+
+
+
-
-
-
- Are you sure you want to decline?
-
-
- This will take you back to the Homepage
-
-
-
- Cancel
- Yes, Decline
-
-
-
-
@@ -276,7 +274,7 @@ export default {
return {
showOverlay: false,
contractItems: reactive([]),
- radios: 0,
+ radios: "0.0",
details: false,
detailsTitle: "More details",
policies: [],
From f6ee0d23bea19989d4b4df9e08e0209e1a763f2e Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Thu, 1 Feb 2024 19:23:32 +0100
Subject: [PATCH 09/23] feat: finished extra details
---
src/views/PassportView.vue | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 8d5a13eae..0aa5e967c 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -46,9 +46,6 @@
-
-
-
@@ -154,6 +151,9 @@
+
+
+
Date: Thu, 1 Feb 2024 19:26:57 +0100
Subject: [PATCH 10/23] feat: finished extra details
---
src/views/PassportView.vue | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 0aa5e967c..1ef816237 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -46,7 +46,10 @@
-
+
+
+
+
@@ -151,9 +154,6 @@
-
-
-
Date: Fri, 2 Feb 2024 08:45:40 +0100
Subject: [PATCH 11/23] feat: supported new backend apis
---
dpp-backend/scripts/get-data.sh | 10 +++---
dpp-backend/scripts/getPassport.py | 30 ++++++++++++------
dpp-backend/scripts/requirements.txt | 1 +
.../authentication.cpython-312.pyc | Bin 0 -> 11690 bytes
.../__pycache__/constants.cpython-312.pyc | Bin 0 -> 1116 bytes
.../__pycache__/httpUtils.cpython-312.pyc | Bin 0 -> 2870 bytes
.../__pycache__/operators.cpython-312.pyc | Bin 0 -> 8525 bytes
dpp-backend/scripts/utilities/constants.py | 6 ++--
8 files changed, 31 insertions(+), 16 deletions(-)
create mode 100644 dpp-backend/scripts/requirements.txt
create mode 100644 dpp-backend/scripts/utilities/__pycache__/authentication.cpython-312.pyc
create mode 100644 dpp-backend/scripts/utilities/__pycache__/constants.cpython-312.pyc
create mode 100644 dpp-backend/scripts/utilities/__pycache__/httpUtils.cpython-312.pyc
create mode 100644 dpp-backend/scripts/utilities/__pycache__/operators.cpython-312.pyc
diff --git a/dpp-backend/scripts/get-data.sh b/dpp-backend/scripts/get-data.sh
index ae70f6e7d..246f1768a 100644
--- a/dpp-backend/scripts/get-data.sh
+++ b/dpp-backend/scripts/get-data.sh
@@ -23,9 +23,11 @@
## this command in Python is recommended to run in UNBUFFERED mode, and to print standard output (stdout/stderr)
export PYTHONUNBUFFERED=TRUE;
+pip install -r requirements.txt --user
+
## execute the python script
-python ./getPassport.py --id BAT-XYZ789 \
- --discoveryId XYZ78901 \
+python ./getPassport.py --id NCM-6789 \
+ --discoveryId MAT7814 \
--company CX-Test-Access \
- --username "" \
- --password ""
+ --username "" \
+ --password ""
diff --git a/dpp-backend/scripts/getPassport.py b/dpp-backend/scripts/getPassport.py
index 3376f207e..689e56ec5 100644
--- a/dpp-backend/scripts/getPassport.py
+++ b/dpp-backend/scripts/getPassport.py
@@ -44,10 +44,9 @@ def get_arguments():
help="password required to login", required=True)
parser.add_argument("-s", "--semanticId", \
- default="urn:bamm:io.catenax.generic.digital_product_passport:1.0.0#DigitalProductPassport", \
help="Semantic ID of the aspect model", required=False)
- parser.add_argument("-it", "--idType", default="partInstanceId", \
+ parser.add_argument("-it", "--idType", \
help="Product type attribute to lookup into digital twin registry", required=False)
parser.add_argument("-id", "--id", help="The product type value to lookup into the digital twin registry", \
@@ -86,13 +85,21 @@ def create_process(manufacturer_part_id, session=None):
except Exception as exception:
op.print_message("Exception occured while creating a process -> " + exception, info_level="[ERROR]", log_enabled=is_log_enabled)
-def search_contract(process_id, serialized_id, id_type, semantic_id, session=None):
+def search_contract(process_id, serialized_id, id_type=None, semantic_id=None, session=None, children=None):
data = {
"id": serialized_id,
- "processId": process_id,
- "idType": id_type,
- "semanticId": semantic_id
+ "processId": process_id
}
+ if(semantic_id is not None):
+ data["semanticId"] = semantic_id
+
+ if(id_type is not None):
+ data["idType"] = id_type
+
+ if(children is not None):
+ data["children"] = children
+
+ print(data)
headers={
"Authorization": "Bearer "+ access_token,
"Content-Type": "application/json"
@@ -115,7 +122,7 @@ def negotiate_contract(process_id, contract_id, token, session=None):
"Authorization": "Bearer "+ access_token,
"Content-Type": "application/json"
}
- url = Constants.SERVER_URL + Constants.SIGN_API
+ url = Constants.SERVER_URL + Constants.AGREE_API
try:
response = HttpUtils.do_post(url=url, session=session, headers=headers, verify=False, json=data)
return response.json()
@@ -196,14 +203,19 @@ def retrieve_passport(process_id, contract_id, token, session=None):
op.print_message("Process created with ID " + process_id, log_enabled=is_log_enabled)
# search for a contract
- negotiation_response = search_contract(process_id, serialized_id, id_type, semantic_id, session)
+ negotiation_response = search_contract(process_id, serialized_id, id_type, semantic_id, session, children)
if ((status and status != 200) or not negotiation_response["data"]):
op.print_message("The contract was not available", info_level="[ERROR]", log_enabled=is_log_enabled)
raise Exception("[ERROR] - The contract was not available")
negotiation = negotiation_response["data"]
token = op.get_attribute(negotiation, "token")
- contract_id = op.get_attribute(negotiation, "contract.@id")
+ contracts = op.get_attribute(negotiation, "contracts")
+
+ #Get first contract key from dictionary
+ firstContract = list(contracts.keys())[0]
+
+ contract_id = op.get_attribute(contracts[firstContract], "@id")
# If token or contract id does not exist -> error is returned
if (not token or not contract_id):
diff --git a/dpp-backend/scripts/requirements.txt b/dpp-backend/scripts/requirements.txt
new file mode 100644
index 000000000..f2293605c
--- /dev/null
+++ b/dpp-backend/scripts/requirements.txt
@@ -0,0 +1 @@
+requests
diff --git a/dpp-backend/scripts/utilities/__pycache__/authentication.cpython-312.pyc b/dpp-backend/scripts/utilities/__pycache__/authentication.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ef31ecebdc07bc1598ff35c0334237b4ae44921a
GIT binary patch
literal 11690
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
O<+PE8cR-PZ^!dNd-pRuN
literal 0
HcmV?d00001
diff --git a/dpp-backend/scripts/utilities/__pycache__/constants.cpython-312.pyc b/dpp-backend/scripts/utilities/__pycache__/constants.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c354414956178193d4065be570afcc5e86269869
GIT binary patch
literal 1116
zcmcIjPixdb6i@bFceneu-P&ra;6dshvOy?PvsbOK^xM3O8fAO!8A|BHt(KYaJ|W6#e;p#)QWkz|$L>eOpN
z79k6d41|I_LL-}uhaoQ@^m>ek*Pd)o?d~Z}pawauy8}9CS5<%HwSU^9ShdsfF29PJTxgD3ap99Sf_%6J#WB@DZIow
zYacT3bk}sKHyBfMXj0Sg2CQ6hlQ*0bH+)AES1#fOvj
zw6em?!pa0p3pEDSjmfiVb$tevd(68moTqBjwe|DV&FPKhNq1VQGIiA*
literal 0
HcmV?d00001
diff --git a/dpp-backend/scripts/utilities/__pycache__/httpUtils.cpython-312.pyc b/dpp-backend/scripts/utilities/__pycache__/httpUtils.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..214b4a54e4c57627fe713379f58236efece1429e
GIT binary patch
literal 2870
zcmb_eO>fjj7#@G`=7TH+g?`Xrz9LMsp%qnIzJjViLW#7c1ym{8)^a@qm~Pg~j2B2)
zBn}+lL~lWrDsku`C_UuJf9R>|p<9bvPraquRFydOeaBwQy9G+5TJk&NXT~$nJoC(J
z|1>;2K;W7f_?~??M9A;>(7lYb()j@@%Y+iD)W|$h$t3AOSAxJF^3uw9RPGNhnf-{0(3ZsS`ys?=tvGNEGkav51hK!ypFOSR!y<
zTVBu3O&_iLUc~C&&2s&6HM&`*!Q~+GYUKtGY15C&4Nr(FZ8XYXd;T}9POHM_K_e2?
zCWsM40Tb0LU1=s8i?K;V7f39XGQlPjWG7C&7s4I#bY#ba*8SEaZRLaZzIWDU+k2fS
zhc*ngG^eZ+wK%7Apd0aV72PY-xeCZK=y+%Q%e7uj-@L7~l&f9;c1ME}_5$}oYZ)yw
zzZV?UaWp;*MQojCLIh!*Z$Z$Ky2h4B!50dQ#1nySJ_2=Kg79xe_5do5LAXQOgRk8?
z`E+#b*U|BxN5_8|J#cTfJu>>>*8N)##mabl@4>YX+j|Z?*|lzv(pw#(md;A0&MJH>
zyx*e~ZnkvfQK&4Fm#dViprksankSx27MvJKdI;SV>(q<9SidSj7xa^(CSvK0P(-On
zd#2#IfQ-@pB6+saIP|HRrC?LFfwr`qM?
z8rh8>_d+)R);kipzyBlj5@F)x1!@J}+{j;`SNIGHDrdBsH9WFGy`eG%FX2NEjE|v2O
z^K3t9wgX`Y_3K_OpmxaZdI&4S?iG%SrJv<~#4wzMyVGTPIAe)Tn_@Y5k=HVYW4#RF
zONQ+JgVF({gkc))Ot^g+C2ZOhrJ?DNvW!!oYG{U#2TRg1pdn?iKoOG<$1sbPaSU?f
z5!w{B;R_*rJu_}jUN31?--oXX7!rED0x$f7;TY!N&ZIk(Nrz3F;wS%aPNVae4)=3Q
z|1NsZ4U#@{;lepPIWEuj+$6HZ@M)gqk&GoaIYs&PkU1bOp1$d`1|Dg7prl`=L*yRF
z1ArzJjqW}{9PClvxoQ+!n&p?N@tQNrN8wt<;0%^!p12@Rf-RfY!E0m3J|^x
Ua&;$H&-eKxhZq0Cccl^j0gE?T7XSbN
literal 0
HcmV?d00001
diff --git a/dpp-backend/scripts/utilities/__pycache__/operators.cpython-312.pyc b/dpp-backend/scripts/utilities/__pycache__/operators.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..150551e5ddb85565a2edee5e93c89bf0c76599d3
GIT binary patch
literal 8525
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
literal 0
HcmV?d00001
diff --git a/dpp-backend/scripts/utilities/constants.py b/dpp-backend/scripts/utilities/constants.py
index 2c4afe5e4..291edd8c8 100644
--- a/dpp-backend/scripts/utilities/constants.py
+++ b/dpp-backend/scripts/utilities/constants.py
@@ -25,14 +25,14 @@ class Constants:
PROVIDER = "https://centralidp.int.demo.catena-x.net"
TOKEN_URI = "https://centralidp.int.demo.catena-x.net/auth/realms/CX-Central/protocol/openid-connect/token"
AUTH_URI = "https://centralidp.int.demo.catena-x.net/auth/realms/CX-Central/protocol/openid-connect/auth"
- REDIRECT_URI = "https://materialpass.int.demo.catena-x.net"
- CLIENT_ID = "Cl13-CX-Battery"
+ REDIRECT_URI = "https://dpp.int.demo.catena-x.net"
+ CLIENT_ID = "app144"
REALM = "CX-Central"
SCOPE = "openid"
SERVER_URL = "https://dpp.int.demo.catena-x.net"
CREATE_API = "/api/contract/create"
SEARCH_API = "/api/contract/search"
- SIGN_API = "/api/contract/sign"
+ AGREE_API = "/api/contract/agree"
CHECK_STATUS_API = "/api/contract/status"
RETRIEVE_PASSPORT_API = "/api/data"
API_MAX_RETRIES = 3
From 9733e279b0c05c1603458936391c45a293b67056 Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 10:07:27 +0100
Subject: [PATCH 12/23] chore: added python cache to ignored files
---
.gitignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 945647994..73b46dafa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
#################################################################################
# Catena-X - Product Passport Consumer Frontend
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -20,6 +20,7 @@
# SPDX-License-Identifier: Apache-2.0
##################################################################################
+__pycache__/
.DS_Store
node_modules
dist
From d7b3d79fd21adc07f88c30b0bd6558c26803fd8b Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 12:57:18 +0100
Subject: [PATCH 13/23] chore: added changes to readme
---
.../http/controllers/api/ContractController.java | 4 ++++
dpp-backend/scripts/README.md | 9 +++++----
dpp-backend/scripts/getPassport.py | 1 -
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
index 5cce5d76c..73d4d97a1 100644
--- a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
+++ b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
@@ -592,6 +592,8 @@ public Response negotiate(@Valid @RequestBody TokenRequest tokenRequestBody) {
response = httpUtil.getForbiddenResponse("This contract was declined! Please request a new one");
return httpUtil.buildResponse(response, httpResponse);
}
+
+ // Check if negotiation is canceled
if (status.historyExists("negotiation-canceled")) {
response = httpUtil.getForbiddenResponse("This negotiation has been canceled! Please request a new one");
return httpUtil.buildResponse(response, httpResponse);
@@ -615,6 +617,8 @@ public Response negotiate(@Valid @RequestBody TokenRequest tokenRequestBody) {
response = httpUtil.getBadRequest("This contract id does not exists!");
return httpUtil.buildResponse(response, httpResponse);
}
+
+ // Load all the available contracts
Map availableContracts = processManager.loadDatasets(processId);
String seedId = String.join("|",availableContracts.keySet()); // Generate Seed
// Check the validity of the token
diff --git a/dpp-backend/scripts/README.md b/dpp-backend/scripts/README.md
index 3965a156a..4e8547aa0 100644
--- a/dpp-backend/scripts/README.md
+++ b/dpp-backend/scripts/README.md
@@ -39,7 +39,8 @@ A command line python script to request for any aspect model data using the Digi
- Export enabled/disabled option to export the requested aspect data to a json file
- Logging enabled/disabled option to log intermediate retrieval status to a file for further backtracking/debugging/troubleshooting
- The backend API and authorization server settings are configurable
-- Capable to handle exception and error messages
+- Capable to handle exception and error messages+
+- When doing contract negotiation the first contract is always choosed
## TL;DR
- The default script configuration is provided in [Constants.py](./utilities/constants.py) and can be changed based on the authentication provider.
@@ -57,10 +58,10 @@ The following parameters can be added in the [test.sh](./test.sh)
| --company | Company name | CX-Test-Access | Required |
| --username | username | your username | Required |
| --password | password | your password | Required |
-| --semanticId | Semantic ID of the aspect model | urn:bamm:io.catenax.generic.digital_product_passport:1.0.0#DigitalProductPassport | Optional |
-| -idType | Product type attribute to lookup into digital twin registry | partInstanceId | Optional |
+| --semanticId | Semantic ID of the aspect model | *Managed by the Backend* | Optional |
+| -idType | Product type attribute to lookup into digital twin registry | *Managed by the Backend*: "partInstanceId" | Optional |
| --id |Product type value to lookup into the digital twin registry | BAT-XYZ789 | Required |
-| --discoveryType | Discovery type attribute to lookup into the discovery service | manufacturerPartId | Optional |
+| --discoveryType | Discovery type attribute to lookup into the discovery service | *Managed by the Backend*: "manufacturerPartId" | Optional |
| --discoveryId | Discovery type value to lookup into the discovery service | XYZ78901 | Required |
| -getChildren | Boolean value to check if children are retrieved | False | Optional |
| | | | |
diff --git a/dpp-backend/scripts/getPassport.py b/dpp-backend/scripts/getPassport.py
index 689e56ec5..688b6e407 100644
--- a/dpp-backend/scripts/getPassport.py
+++ b/dpp-backend/scripts/getPassport.py
@@ -214,7 +214,6 @@ def retrieve_passport(process_id, contract_id, token, session=None):
#Get first contract key from dictionary
firstContract = list(contracts.keys())[0]
-
contract_id = op.get_attribute(contracts[firstContract], "@id")
# If token or contract id does not exist -> error is returned
From 5063a3ca08c63be575fb46c8995503683095fbd6 Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 13:34:04 +0100
Subject: [PATCH 14/23] chore: added error message when semantic id is not
available
---
src/utils/configUtil.js | 4 ++
src/views/PassportView.vue | 144 ++++++++++++++++++-------------------
2 files changed, 74 insertions(+), 74 deletions(-)
diff --git a/src/utils/configUtil.js b/src/utils/configUtil.js
index eb2d00ec4..b889c2947 100644
--- a/src/utils/configUtil.js
+++ b/src/utils/configUtil.js
@@ -48,6 +48,10 @@ export default {
return tmpPropsData;
}*/
normalizePassport(responsePassport=null, responseMetadata=null, semanticId="urn:bamm:io.catenax.generic.digital_product_passport:1.0.0#DigitalProductPassport"){
+ if(!jsonUtil.exists(semanticId, passports)){
+ console.error("[ERROR] Semantic Id ["+semanticId+"] is not available in the passport templates and is not supported by the application");
+ return null;
+ }
let passport = passports[semanticId]; //Get the passport by semanticId
let response = {
"metadata": metadata,
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 1ef816237..612da5f0d 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -1,7 +1,7 @@
@@ -71,7 +71,9 @@
@click="chooseContract(contractId, item['@id'])"
:value="`${contractIndex}.${index}`"
:label="
- 'Policy [' + index + '] type: ' +
+ 'Policy [' +
+ index +
+ '] type: ' +
(item['odrl:permission']['odrl:action']['odrl:type'] !=
undefined
? item['odrl:permission']['odrl:action']['odrl:type']
@@ -81,76 +83,78 @@
+
+ Decline
+ Agree
+
+
+
+ {{ detailsTitle }}
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to decline?
+
+
+
+ This will take you back to the Homepage
+
+
DeclineCancel
Agree
-
-
- Yes, Decline
- {{ detailsTitle }}
-
-
-
-
-
-
-
-
- Are you sure you want to decline?
-
-
- This will take you back to the Homepage
-
-
-
- Cancel
- Yes, Decline
-
-
-
+
@@ -414,9 +418,6 @@ export default {
}
},
chooseContract(contract, policy) {
- console.log("Contract chosen - contractToSign:", this.contractToSign);
- console.log("Contract chosen - policies:", this.policies);
-
return (this.contractToSign = store.commit("setContractToSign", {
contract: contract,
policy: policy,
@@ -554,7 +555,7 @@ export default {
let contracts = jsonUtil.get("data.contracts", searchResponse);
let token = jsonUtil.get("data.token", searchResponse);
let processId = jsonUtil.get("data.id", searchResponse);
-
+
try {
// Setup aspect promise
let passportPromise = this.negotiatePassport(
@@ -582,6 +583,7 @@ export default {
} catch (e) {
console.log("passportView -> " + e);
} finally {
+ console.log("DATA:");
console.log(this.data);
if (
this.data &&
@@ -641,8 +643,6 @@ export default {
return response;
}
- // response = jsonUtil.copy(response, true);
-
// Check if the response is empty and give an error
if (!response) {
this.errorObj.title = "Failed to return passport";
@@ -710,8 +710,6 @@ export default {
return response;
}
- // response = jsonUtil.copy(response, true);
-
// Check if the response is empty and give an error
if (!response) {
this.errorObj.title = "Failed to return passport";
@@ -771,8 +769,6 @@ export default {
return response;
}
- // response = jsonUtil.copy(response, true);
-
// Check if the response is empty and give an error
if (!response) {
this.errorObj.title = "Failed to return passport";
From aec3f0f493ecd4020f5c87aa4f2f0307e0c7385e Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 13:35:51 +0100
Subject: [PATCH 15/23] chore: removed debuggers
---
.../digitalproductpass/http/controllers/AppController.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/AppController.java b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/AppController.java
index f191b3c71..c31ae57bb 100644
--- a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/AppController.java
+++ b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/AppController.java
@@ -154,7 +154,6 @@ public Response health() {
@Operation(summary = "Receives the EDR for the EDC Consumer and queries for the dDTR")
public Response getDigitalTwin(@RequestBody Object body, @PathVariable String processId, @PathVariable String endpointId) {
try {
- System.out.println(jsonUtil.toJson(body, true));
DataPlaneEndpoint endpointData = null;
try {
endpointData = this.getEndpointData(body);
@@ -308,7 +307,6 @@ public DataPlaneEndpoint getEndpointData(Object body) throws ControllerException
@Operation(summary = "Receives the EDR from the EDC Consumer and get the passport json")
public Response endpoint(@RequestBody Object body, @PathVariable String processId) {
try {
- System.out.println(jsonUtil.toJson(body, true));
DataPlaneEndpoint endpointData = null;
try {
endpointData = this.getEndpointData(body);
From ef029bdcd30735101d89dbd93e61536fabf637ae Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 15:14:11 +0100
Subject: [PATCH 16/23] chore: removed debugging logs
---
src/services/BackendService.js | 1 -
src/views/PassportView.vue | 2 --
2 files changed, 3 deletions(-)
diff --git a/src/services/BackendService.js b/src/services/BackendService.js
index 135c4e7e2..b58af8a26 100644
--- a/src/services/BackendService.js
+++ b/src/services/BackendService.js
@@ -152,7 +152,6 @@ export default class BackendService {
}
async negotiateAsset(contracts, token, processId, authentication, contractId=null, policyId=null){
let contract = null;
- console.log("Contracts: " + contracts);
// Use selects here a contract
if(contractId == null){
contract = contracts[Object.keys(contracts)[0]];
diff --git a/src/views/PassportView.vue b/src/views/PassportView.vue
index 612da5f0d..6609973ba 100644
--- a/src/views/PassportView.vue
+++ b/src/views/PassportView.vue
@@ -583,8 +583,6 @@ export default {
} catch (e) {
console.log("passportView -> " + e);
} finally {
- console.log("DATA:");
- console.log(this.data);
if (
this.data &&
jsonUtil.exists("status", this.data) &&
From cd40fc9ac8610df33f8ecde1df116a103faed1cf Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 15:21:18 +0100
Subject: [PATCH 17/23] chore: added retry when edc is not reachable
---
.../http/controllers/api/ContractController.java | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
index 73d4d97a1..d0bbc94b0 100644
--- a/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
+++ b/dpp-backend/digitalproductpass/src/main/java/org/eclipse/tractusx/digitalproductpass/http/controllers/api/ContractController.java
@@ -359,10 +359,14 @@ public Response search(@Valid @RequestBody Search searchBody) {
try {
datasets = dataService.getContractOffersByAssetId(assetId, connectorAddress);
} catch (ServiceException e) {
- response.message = "The EDC is not reachable, it was not possible to retrieve catalog! Please try again!";
- response.status = 502;
- response.statusText = "Bad Gateway";
- return httpUtil.buildResponse(response, httpResponse);
+ LogUtil.printError("The EDC is not reachable, it was not possible to retrieve catalog! Trying again...");
+ datasets = dataService.getContractOffersByAssetId(assetId, connectorAddress);
+ if (datasets == null) { // If the contract catalog is not reachable retry...
+ response.message = "The EDC is not reachable, it was not possible to retrieve catalog! Please try again!";
+ response.status = 502;
+ response.statusText = "Service Not Available";
+ return httpUtil.buildResponse(response, httpResponse);
+ }
}
// Check if contract offer was not received
if (datasets == null) {
From d47c53d1a8acb3f22576bad2b89d245528f4da1f Mon Sep 17 00:00:00 2001
From: Mathias Brunkow Moser
Date: Fri, 2 Feb 2024 15:46:33 +0100
Subject: [PATCH 18/23] merge: merged develop into integration, resolved
conflicts
---
.github/actions/setup-java/action.yaml | 13 +-
.github/workflows/kics.yml | 26 +-
.../publish-dpp-backend-docker-image.yml | 37 +-
.../publish-dpp-frontend-docker-image.yml | 23 +-
.github/workflows/publish-swagger-hub.yaml | 21 +-
.github/workflows/trivy.yml | 27 +-
.trivyignore | 32 ++
Dockerfile | 17 +-
README.md | 40 +-
.../templates/deployment-backend.yaml | 18 +-
.../templates/deployment-frontend.yaml | 10 +-
charts/digital-product-pass/values.yaml | 62 +++
deployment/README.md | 16 +-
deployment/images/components.png | Bin 0 -> 94827 bytes
deployment/images/dashboard.png | Bin 917659 -> 753673 bytes
deployment/images/passport-scan.png | Bin 0 -> 211431 bytes
deployment/images/product-passport.png | Bin 0 -> 96335 bytes
deployment/images/scan-passport.png | Bin 324550 -> 0 bytes
.../data-service/templates/deployment.yaml | 6 +
.../edc-provider/data-service/values.yaml | 43 ++
.../testing/testdata/testdata-payload.json | 366 +++++++++++++++++
docs/GETTING-STARTED.md | 4 +-
docs/README.md | 4 +-
docs/admin guide/Admin_Guide.md | 4 +-
docs/arc42/Arc42.md | 2 +-
docs/business statement/Business Statement.md | 22 +-
.../GraphArchitectureFunctionalCut.png | Bin 401884 -> 0 bytes
.../GraphArchitectureFunctionalCut.svg | 4 +
.../GraphBusinessProblem.png | Bin 152152 -> 0 bytes
.../GraphBusinessProblem.svg | 4 +
docs/notice.md | 57 +++
docs/user manual/MainMenu.png | Bin 760140 -> 0 bytes
docs/user manual/ProductPassport.png | Bin 362850 -> 0 bytes
.../User Manual Product Viewer App.md | 82 ++--
docs/user manual/UserProfile.png | Bin 19964 -> 0 bytes
docs/user manual/images/IRS.png | Bin 0 -> 49764 bytes
docs/user manual/images/IRSJob.png | Bin 0 -> 423367 bytes
docs/user manual/images/LoadingPass.png | Bin 0 -> 256631 bytes
docs/user manual/images/MainMenu.png | Bin 0 -> 805214 bytes
docs/user manual/images/ProductPassport.png | Bin 0 -> 117497 bytes
docs/user manual/images/ScanPassport.png | Bin 0 -> 211431 bytes
docs/user manual/images/UserProfile.png | Bin 0 -> 15972 bytes
docs/user manual/scan-passport.png | Bin 324550 -> 0 bytes
.../digital-product-pass-backend/values.yaml | 6 +-
dpp-backend/digitalproductpass/Dockerfile | 13 +-
dpp-backend/digitalproductpass/README.md | 386 ++++++++++++++++++
.../digitalproductpass/docs/media/img2.png | Bin 75208 -> 70085 bytes
.../docs/tests/UNIT_TESTS.md | 280 +++++++++++++
.../digitalproductpass/docs/tests/unitTest.md | 144 -------
dpp-backend/digitalproductpass/readme.md | 213 +++++++---
.../java/managers/ProcessManagerTest.java | 5 -
51 files changed, 1616 insertions(+), 371 deletions(-)
create mode 100644 .trivyignore
create mode 100644 deployment/images/components.png
create mode 100644 deployment/images/passport-scan.png
create mode 100644 deployment/images/product-passport.png
delete mode 100644 deployment/images/scan-passport.png
delete mode 100644 docs/business statement/GraphArchitectureFunctionalCut.png
create mode 100644 docs/business statement/GraphArchitectureFunctionalCut.svg
delete mode 100644 docs/business statement/GraphBusinessProblem.png
create mode 100644 docs/business statement/GraphBusinessProblem.svg
create mode 100644 docs/notice.md
delete mode 100644 docs/user manual/MainMenu.png
delete mode 100644 docs/user manual/ProductPassport.png
delete mode 100644 docs/user manual/UserProfile.png
create mode 100644 docs/user manual/images/IRS.png
create mode 100644 docs/user manual/images/IRSJob.png
create mode 100644 docs/user manual/images/LoadingPass.png
create mode 100644 docs/user manual/images/MainMenu.png
create mode 100644 docs/user manual/images/ProductPassport.png
create mode 100644 docs/user manual/images/ScanPassport.png
create mode 100644 docs/user manual/images/UserProfile.png
delete mode 100644 docs/user manual/scan-passport.png
create mode 100644 dpp-backend/digitalproductpass/README.md
create mode 100644 dpp-backend/digitalproductpass/docs/tests/UNIT_TESTS.md
delete mode 100644 dpp-backend/digitalproductpass/docs/tests/unitTest.md
diff --git a/.github/actions/setup-java/action.yaml b/.github/actions/setup-java/action.yaml
index 689cdf714..e6c2c8847 100644
--- a/.github/actions/setup-java/action.yaml
+++ b/.github/actions/setup-java/action.yaml
@@ -1,7 +1,8 @@
#################################################################################
# Catena-X - Product Passport Consumer Application
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -21,13 +22,13 @@
#################################################################################
---
-name: "Setup Java 19"
-description: "Setup Java 19"
+name: "Setup Java"
+description: "Setup Java"
runs:
using: "composite"
steps:
- name: Setup Java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
- java-version: '19'
- distribution: 'adopt'
+ java-version: '21'
+ distribution: 'temurin'
diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml
index ae92abd1b..b2bca619c 100644
--- a/.github/workflows/kics.yml
+++ b/.github/workflows/kics.yml
@@ -25,14 +25,16 @@ name: "KICS"
on:
push:
branches: [ main ]
- # pull_request:
- # The branches below must be a subset of the branches above
- # branches: [main, master]
- # paths-ignore:
- # - "**/*.md"
- # - "**/*.txt"
+ paths-ignore:
+ - '**/*.md'
+ - '**/*.txt'
+ pull_request:
+ branches: [ main ]
+ paths-ignore:
+ - "**/*.md"
+ - "**/*.txt"
schedule:
- - cron: "0 0 * * *"
+ - cron: "0 0 * * *" # Once a day
jobs:
analyze:
@@ -44,7 +46,7 @@ jobs:
security-events: write
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: KICS scan
uses: checkmarx/kics-github-action@master
@@ -74,4 +76,10 @@ jobs:
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: kicsResults/results.sarif
-
+
+ - name: Archive results
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: kicsResults.json
+ path: kicsResults/results.json
diff --git a/.github/workflows/publish-dpp-backend-docker-image.yml b/.github/workflows/publish-dpp-backend-docker-image.yml
index 30eaf7a41..6526dfabc 100644
--- a/.github/workflows/publish-dpp-backend-docker-image.yml
+++ b/.github/workflows/publish-dpp-backend-docker-image.yml
@@ -1,7 +1,8 @@
#################################################################################
-# Catena-X - Product Passport Consumer Application
+# Catena-X - Digital Product Pass Application
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -43,7 +44,6 @@ on:
env:
IMAGE_NAME: 'digital-product-pass-backend'
- JAVA_VERSION: 19
REGISTRY: 'ghcr.io'
IMAGE_NAMESPACE: 'tractusx'
@@ -55,15 +55,18 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
+ - name: Setup Java
+ uses: ./.github/actions/setup-java
+
# Build actions for GHCR registry
- name: Docker meta for GHCR
id: meta-for-ghcr
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}
@@ -78,16 +81,11 @@ jobs:
# https://github.com/docker/login-action
- name: Log into GHCR registry
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
- - uses: actions/setup-java@v3
- with:
- java-version: '${{ env.JAVA_VERSION }}'
- distribution: 'adopt'
# Build Java code with Maven
- name: Build dpp backend with maven for GHCR registry
@@ -101,7 +99,7 @@ jobs:
- name: Build and push backend for GHCR registry
id: build-and-push-backend-ghcr
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
context: dpp-backend/digitalproductpass
push: true
@@ -112,7 +110,7 @@ jobs:
- name: Docker meta for Docker Hub
id: meta-for-dockerhub
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: |
${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }}
@@ -127,15 +125,10 @@ jobs:
# https://github.com/docker/login-action
- name: Log into a Docker registry
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
-
- - uses: actions/setup-java@v3
- with:
- java-version: '${{ env.JAVA_VERSION }}'
- distribution: 'adopt'
# Build Java code with Maven
- name: Build dpp backend with maven for Docker Hub
@@ -149,7 +142,7 @@ jobs:
- name: Build and push backend for Docker Hub
id: build-and-push-backend-dockerhub
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
context: dpp-backend/digitalproductpass
push: true
@@ -160,10 +153,10 @@ jobs:
# Important step to push image description to DockerHub
- name: Update Docker Hub description
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: peter-evans/dockerhub-description@v3
+ uses: peter-evans/dockerhub-description@v4
with:
# readme-filepath defaults to toplevel README.md, Only necessary if you have a dedicated file with your 'Notice for docker images'
- # readme-filepath: path/to/dedicated/notice-for-docker-image.md
+ readme-filepath: docs/notice.md
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
repository: ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }}
diff --git a/.github/workflows/publish-dpp-frontend-docker-image.yml b/.github/workflows/publish-dpp-frontend-docker-image.yml
index 84d3819a6..a5cbc802a 100644
--- a/.github/workflows/publish-dpp-frontend-docker-image.yml
+++ b/.github/workflows/publish-dpp-frontend-docker-image.yml
@@ -1,7 +1,8 @@
#################################################################################
-# Catena-X - Product Passport Consumer Application
+# Catena-X - Digital Product Pass Application
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -56,7 +57,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
@@ -64,7 +65,7 @@ jobs:
- name: Docker meta for GHCR
id: meta-for-ghcr
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}
@@ -79,7 +80,7 @@ jobs:
# https://github.com/docker/login-action
- name: Log into GHCR registry
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -90,7 +91,7 @@ jobs:
- name: Build and push frontend for GHCR registry
id: build-and-push-frontend-ghcr
if: ${{ github.repository != 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
push: true
tags: ${{ steps.meta-for-ghcr.outputs.tags }}, ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}:latest
@@ -103,7 +104,7 @@ jobs:
- name: Docker meta for Docker Hub
id: meta-for-dockerhub
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: |
${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }}
@@ -118,7 +119,7 @@ jobs:
# https://github.com/docker/login-action
- name: Log into a Docker registry
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
@@ -128,7 +129,7 @@ jobs:
- name: Build and push frontend for Docker Hub
id: build-and-push-frontend-dockerhub
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
push: true
tags: ${{ steps.meta-for-dockerhub.outputs.tags }}, ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }}:latest
@@ -142,10 +143,10 @@ jobs:
# Important step to push image description to DockerHub
- name: Update Docker Hub description
if: ${{ github.repository == 'eclipse-tractusx/digital-product-pass' }}
- uses: peter-evans/dockerhub-description@v3
+ uses: peter-evans/dockerhub-description@v4
with:
# readme-filepath defaults to toplevel README.md, Only necessary if you have a dedicated file with your 'Notice for docker images'
- # readme-filepath: path/to/dedicated/notice-for-docker-image.md
+ readme-filepath: docs/notice.md
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
repository: ${{ env.IMAGE_NAMESPACE }}/${{ env.IMAGE_NAME }}
diff --git a/.github/workflows/publish-swagger-hub.yaml b/.github/workflows/publish-swagger-hub.yaml
index 59150297c..b5c416e8d 100644
--- a/.github/workflows/publish-swagger-hub.yaml
+++ b/.github/workflows/publish-swagger-hub.yaml
@@ -1,7 +1,8 @@
#################################################################################
-# Catena-X - Product Passport Consumer Application
+# Catena-X - Digital Product Passport Application
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -32,7 +33,6 @@ on:
inputs:
version:
required: false
- default: 'main'
type: string
workflow_dispatch:
@@ -40,7 +40,6 @@ on:
version:
required: false
description: "Version of the DPP API is to be published"
- default: 'main'
type: string
jobs:
@@ -49,6 +48,7 @@ jobs:
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }}
+
steps:
- uses: actions/checkout@v4
@@ -60,10 +60,19 @@ jobs:
- name: Install Swagger CLI
run: |
npm i -g swaggerhub-cli
+
+ - name: Get version tag
+ id: version
+ run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- - name: Extract versions
+ - name: Set version tag
run: |
- export DOWNSTREAM_VERSION=${{ inputs.version }}
+ if [ -z ${{ inputs.version }} ]; then
+
+ export DOWNSTREAM_VERSION=${{ steps.version.outputs.tag }}
+ else
+ export DOWNSTREAM_VERSION=${{ inputs.version }}
+ fi
echo "[INFO] - DOWNSTREAM_VERSION=$DOWNSTREAM_VERSION" >> "$GITHUB_ENV"
- name: Create and Download API specs
diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml
index ba19a5a3a..3228a3be9 100644
--- a/.github/workflows/trivy.yml
+++ b/.github/workflows/trivy.yml
@@ -2,6 +2,7 @@
# Catena-X - Product Passport Consumer Application
#
# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -64,16 +65,16 @@ jobs:
uses: aquasecurity/trivy-action@master
with:
scan-type: "config"
- # ignore-unfixed: true
- exit-code: "1"
+ ignore-unfixed: true
hide-progress: false
- # image-ref: "${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
format: "sarif"
output: "trivy-results1.sarif"
severity: "CRITICAL,HIGH"
+ args: "--ignorefile .trivyignore"
+ skip-dirs: "deployment/infrastructure/edc-consumer,deployment/infrastructure/edc-provider,deployment/infrastructure/edc-provider/data-service,deployment/infrastructure/registry" # skip scanning external images.
- name: Upload Trivy scan results to GitHub Security tab
- uses: github/codeql-action/upload-sarif@v2
+ uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-results1.sarif"
@@ -107,10 +108,11 @@ jobs:
with:
image-ref: "${{ env.REGISTRY }}/${{ github.repository }}/${{ env.FRONTEND_IMAGE_NAME }}"
format: "sarif"
+ ignore-unfixed: true
output: "trivy-results-dpp-frontend.sarif"
- exit-code: "1"
hide-progress: false
severity: "CRITICAL,HIGH"
+ args: "--ignorefile .trivyignore"
# Build action for docker hub registry
- name: Run Trivy vulnerability scanner - Docker Hub
@@ -119,15 +121,16 @@ jobs:
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ env.IMAGE_NAMESPACE }}/${{ env.FRONTEND_IMAGE_NAME }}"
+ ignore-unfixed: true
format: "sarif"
output: "trivy-results-dpp-frontend.sarif"
- exit-code: "1"
hide-progress: false
severity: "CRITICAL,HIGH"
+ args: "--ignorefile .trivyignore"
- name: Upload Trivy scan results to GitHub Security tab
if: always()
- uses: github/codeql-action/upload-sarif@v2
+ uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results-dpp-frontend.sarif"
@@ -142,7 +145,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
java-version: "${{ env.JAVA_VERSION }}"
distribution: "temurin"
@@ -175,10 +178,11 @@ jobs:
with:
image-ref: "${{ env.REGISTRY }}/${{ github.repository }}/${{ env.BACKEND_IMAGE_NAME }}"
format: "sarif"
+ ignore-unfixed: true
output: "trivy-results-dpp-backend.sarif"
- exit-code: "1"
hide-progress: false
severity: "CRITICAL,HIGH"
+ args: "--ignorefile .trivyignore"
# Build action for docker hub registry
- name: Run Trivy vulnerability scanner - Docker Hub
@@ -188,14 +192,15 @@ jobs:
with:
image-ref: "${{ env.IMAGE_NAMESPACE }}/${{ env.BACKEND_IMAGE_NAME }}"
format: "sarif"
+ ignore-unfixed: true
output: "trivy-results-dpp-backend.sarif"
- exit-code: "1"
hide-progress: false
severity: "CRITICAL,HIGH"
+ args: "--ignorefile .trivyignore"
- name: Upload Trivy scan results to GitHub Security tab
if: always()
- uses: github/codeql-action/upload-sarif@v2
+ uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results-dpp-backend.sarif"
diff --git a/.trivyignore b/.trivyignore
new file mode 100644
index 000000000..90a91b11d
--- /dev/null
+++ b/.trivyignore
@@ -0,0 +1,32 @@
+#################################################################################
+# Catena-X - Product Passport Consumer Frontend
+#
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# 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.
+#
+# 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 govern in permissions and limitations
+# under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+##################################################################################
+
+# List of false positives
+CVE-2023-5363
+CVE-2023-0464
+CVE-2023-44487
+CVE-2023-51074
+CVE-2023-33201
+AVD-KSV-0109
+KSV014
+KSV117
diff --git a/Dockerfile b/Dockerfile
index 5e6abcca8..22e8920e5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,8 @@
#################################################################################
-# Catena-X - Product Passport Consumer Frontend
+# Catena-X - Digital Product Pass Frontend Application
#
-# Copyright (c) 2022, 2023 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 BASF SE, BMW AG, Henkel AG & Co. KGaA
+# Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
@@ -40,7 +41,7 @@ COPY . .
RUN npm run build
-FROM nginxinc/nginx-unprivileged:stable-alpine
+FROM nginxinc/nginx-unprivileged:alpine
ARG REPO_COMMIT_ID='REPO_COMMIT_ID'
ARG REPO_ENDPOINT_URL='REPO_ENDPOINT_URL'
@@ -50,7 +51,7 @@ ENV REPO_ENDPOINT_URL=${REPO_ENDPOINT_URL}
USER root
RUN addgroup -g 3000 appgroup \
- && adduser -u 10000 -g 3000 -h /home/appuser -D appuser
+ && adduser -u 1000 -g 3000 -h /home/nonroot -D nonroot
COPY ./entrypoint.sh /entrypoint.sh
@@ -62,15 +63,15 @@ COPY --from=builder /app/dist /usr/share/nginx/html
HEALTHCHECK NONE
# add permissions for a user
-RUN chown -R 10000:3000 /app && chmod -R 775 /app/
-RUN chown 10000:3000 /entrypoint.sh && chmod -R 775 /entrypoint.sh
+RUN chown -R 1000:3000 /app && chmod -R 775 /app/
+RUN chown 1000:3000 /entrypoint.sh && chmod -R 775 /entrypoint.sh
# Install bash for env variables inject script
RUN apk update && apk add --no-cache bash
# Make nginx owner of /usr/share/nginx/html/ and change to nginx user
-RUN chown -R 10000:3000 /usr/share/nginx/html/ && chmod -R 775 /usr/share/nginx/html/
+RUN chown -R 1000:3000 /usr/share/nginx/html/ && chmod -R 775 /usr/share/nginx/html/
-USER 10000:3000
+USER 1000:3000
EXPOSE 8080
diff --git a/README.md b/README.md
index 1f00af39d..0720cda51 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ To get started you can have a look into our documentation:
| [Arc42](./docs/arc42/Arc42.md) | Main Architecture Document (Arc42) of Digital Product Pass Application |
| [Administration Guide](./docs/admin%20guide/Admin_Guide.md) | Administration Guide explaining the infrastructure and how to configure the application |
| [Data Retrieval Guide](./docs/data%20retrieval%20guide/DataRetrievalGuide.md) | Guide on how to retrieve data from the Catena-X Network as the Digital Product Pass |
-| [Backend Documentation](./dpp-backend/digitalproductpass/readme.md) | Backend documentation Product Passport App |
+| [Backend Documentation](./dpp-backend/digitalproductpass/README.md) | Backend documentation Product Passport App |
| [Deployment in Hotel Budapest](./deployment/README.md) | Technical Guide - Deployment in ArgoCD Hotel Budapest (integration environment) |
| [Docker Overview](./docker/README.md) | Overview on general docker commands |
| [Keycloak Overview](./deployment/local/docker/Keycloak/README.md) | This guide describes how to setup a keycloak instance in local docker container and import existing realm.json file. |
@@ -88,40 +88,30 @@ For installing the Digital Product Pass application please consult our [Intallat
[Apache-2.0](https://raw.githubusercontent.com/eclipse-tractusx/digital-product-pass/main/LICENSE)
-## Notice for Docker images
-DockerHub:
+## Notice for Docker image
+
+DockerHub:
- https://hub.docker.com/r/tractusx/digital-product-pass-frontend
- https://hub.docker.com/r/tractusx/digital-product-pass-backend
-Eclipse Tractus-X product(s) installed within the image:
-
-- GitHub: https://github.com/eclipse-tractusx/digital-product-pass
-- Project home: https://projects.eclipse.org/projects/automotive.tractusx
-- Dockerfiles:
- - Frontend: https://github.com/eclipse-tractusx/digital-product-pass/blob/main/Dockerfile
- - Backend: https://github.com/eclipse-tractusx/digital-product-pass/blob/main/dpp-backend/digitalproductpass/Dockerfile
-- Project License: [Apache License, Version 2.0](https://raw.githubusercontent.com/eclipse-tractusx/digital-product-pass/main/LICENSE)
-
-
-**Used base image**
-- [node:lts-alpine](https://github.com/nodejs/docker-node)
-- [nginxinc/nginx-unprivileged:stable-alpine](https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/Dockerfile-alpine.template)
-- [eclipse-temurin:19-alpine](https://github.com/adoptium/containers)
-- Official DockerHub pages:
+**Base images:**
+- DockerHub:
- Node: https://hub.docker.com/_/node
- Nginxinc/nginx-unprivileged: https://hub.docker.com/r/nginxinc/nginx-unprivileged
- - Eclipse Temurin: https://hub.docker.com/_/eclipse-temurin
-- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin
-- Additional information about images:
+ - Eclipse Temurin: https://hub.docker.com/_/eclipse-temurin
+
+- Dockerfiles:
+ - [node:lts-alpine](https://github.com/nodejs/docker-node)
+ - [nginxinc/nginx-unprivileged:stable-alpine](https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/Dockerfile-alpine.template)
+ - [eclipse-temurin:19-alpine](https://github.com/adoptium/containers)
+
+- GitHub project:
- Node: https://github.com/docker-library/repo-info/tree/master/repos/node
- - Nginxinc/nginx-unprivileged: https://github.com/nginxinc/docker-nginx-unprivileged
+ - nginxinc/docker-nginx-unprivileged: https://github.com/nginxinc/docker-nginx-unprivileged
- Eclipse Temurin: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin
-As with all Docker images, these likely also contain other software which may be under other licenses
-(such as Bash, etc. from the base distribution, along with any direct or indirect dependencies of the primary software being contained).
-As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within.
diff --git a/charts/digital-product-pass/templates/deployment-backend.yaml b/charts/digital-product-pass/templates/deployment-backend.yaml
index 4ddacb504..2760cc6f9 100644
--- a/charts/digital-product-pass/templates/deployment-backend.yaml
+++ b/charts/digital-product-pass/templates/deployment-backend.yaml
@@ -44,16 +44,13 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
- runAsUser: 10000
- fsGroup: 3000
+ {{- toYaml .Values.backend.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Values.backend.name }}
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
securityContext:
- allowPrivilegeEscalation: false
- runAsUser: 10000
- runAsGroup: 3000
+ {{- toYaml .Values.backend.securityContext | nindent 12 }}
env:
- name: "appId"
valueFrom:
@@ -86,9 +83,16 @@ spec:
- name: pvc-backend
mountPath: /app/data/process
subPath: data/process
- - name: pvc-backend
+ - name: tmpfs
mountPath: /app/log
subPath: log
+ - name: tmpfs
+ mountPath: /tmp
+ - name: tmpfs
+ mountPath: /app/data/VaultConfig
+ subPath: VaultConfig/vault.token.yml
+ - name: tmpfs
+ mountPath: /app/tmp
ports:
- containerPort: 8888
name: http
@@ -114,5 +118,7 @@ spec:
- name: pvc-backend
persistentVolumeClaim:
claimName: pvc-data
+ - name: tmpfs
+ emptyDir: {}
diff --git a/charts/digital-product-pass/templates/deployment-frontend.yaml b/charts/digital-product-pass/templates/deployment-frontend.yaml
index 893746da0..f7d8bf8d0 100644
--- a/charts/digital-product-pass/templates/deployment-frontend.yaml
+++ b/charts/digital-product-pass/templates/deployment-frontend.yaml
@@ -44,16 +44,13 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
- runAsUser: 10000
- fsGroup: 3000
+ {{- toYaml .Values.frontend.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Values.frontend.name }}
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
- securityContext:
- allowPrivilegeEscalation: false
- runAsUser: 10000
- runAsGroup: 3000
+ securityContext:
+ {{- toYaml .Values.frontend.securityContext | nindent 12 }}
env:
{{- with (first .Values.frontend.ingress.hosts) }}
- name: "SERVER_URL"
@@ -116,7 +113,6 @@ spec:
- name: "VERSION"
value: "{{ .Chart.AppVersion }}"
-
ports:
- containerPort: 8080
name: http
diff --git a/charts/digital-product-pass/values.yaml b/charts/digital-product-pass/values.yaml
index 6b57824ba..60122b6db 100644
--- a/charts/digital-product-pass/values.yaml
+++ b/charts/digital-product-pass/values.yaml
@@ -51,6 +51,37 @@ backend:
- path: /
pathType: Prefix
+ # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment
+ podSecurityContext:
+ seccompProfile:
+ # -- Restrict a Container's Syscalls with seccomp
+ type: RuntimeDefault
+ # -- Runs all processes within a pod with a special uid
+ runAsUser: 1000
+ # -- Processes within a pod will belong to this guid
+ runAsGroup: 3000
+ # -- The owner for volumes and any files created within volumes will belong to this guid
+ fsGroup: 3000
+
+ # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod
+ securityContext:
+ capabilities:
+ # -- Specifies which capabilities to drop to reduce syscall attack surface
+ drop:
+ - ALL
+ # -- Specifies which capabilities to add to issue specialized syscalls
+ add: []
+ # -- Whether the root filesystem is mounted in read-only mode
+ readOnlyRootFilesystem: true
+ # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID
+ allowPrivilegeEscalation: false
+ # -- Requires the container to run without root privileges
+ runAsNonRoot: true
+ # -- The container's process will run with the specified uid
+ runAsUser: 1000
+ # -- The owner for volumes and any files created within volumes will belong to this guid
+ runAsGroup: 3000
+
# -- in this section we configure the values that are inserted as secrets in the backend
edc:
# -- the secret for assesing the edc management API
@@ -150,6 +181,37 @@ frontend:
ingress:
enabled: false
hosts: []
+
+ # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment
+ podSecurityContext:
+ seccompProfile:
+ # -- Restrict a Container's Syscalls with seccomp
+ type: RuntimeDefault
+ # -- Runs all processes within a pod with a special uid
+ runAsUser: 1000
+ # -- Processes within a pod will belong to this guid
+ runAsGroup: 3000
+ # -- The owner for volumes and any files created within volumes will belong to this guid
+ fsGroup: 3000
+
+ # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod
+ securityContext:
+ capabilities:
+ # -- Specifies which capabilities to drop to reduce syscall attack surface
+ drop:
+ - ALL
+ # -- Specifies which capabilities to add to issue specialized syscalls
+ add: []
+ # -- Whether the root filesystem is mounted in read-only mode
+ readOnlyRootFilesystem: false
+ # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID
+ allowPrivilegeEscalation: false
+ # -- Requires the container to run without root privileges
+ runAsNonRoot: true
+ # -- The container's process will run with the specified uid
+ runAsUser: 1000
+ # -- The owner for volumes and any files created within volumes will belong to this guid
+ runAsGroup: 3000
# -- product passport UI configuration
diff --git a/deployment/README.md b/deployment/README.md
index 771c3c61e..3140ebf9d 100644
--- a/deployment/README.md
+++ b/deployment/README.md
@@ -1,7 +1,8 @@
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