Skip to content

Commit

Permalink
Fix: #42 Implementation for configuring DW instance for dataspace
Browse files Browse the repository at this point in the history
  • Loading branch information
albinpa authored and georgepadayatti committed Apr 3, 2024
1 parent 3738d43 commit b5b7c92
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 50 deletions.
121 changes: 82 additions & 39 deletions connection/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from .models import Connection
from uuid import uuid4
from dataspace_backend.utils import paginate_queryset
from dataspace_backend.settings import DATA_MARKETPLACE_DW_URL, DATA_MARKETPLACE_APIKEY
import requests

# Create your views here.

Expand All @@ -19,29 +21,46 @@ def post(self, request):
try:
datasource = DataSource.objects.get(admin=request.user)
except DataSource.DoesNotExist:
return JsonResponse({'error': 'Data source not found'}, status=status.HTTP_400_BAD_REQUEST)
return JsonResponse(
{"error": "Data source not found"}, status=status.HTTP_400_BAD_REQUEST
)

# Call digital wallet to create connection
# Add dummy connection
connection_id = str(uuid4())
response = {
"connection": {
"connectionId": connection_id,
"invitation": {
"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation",
"@id": "7b4e6658-1489-4b2d-9716-f5ac872da95d",
"serviceEndpoint": "https://cloudagent.igrant.io/v1/64ec561de2f6a8000142c671/agent/",
"label": "Jacobsons lumber yard",
"imageUrl": "https://staging-api.igrant.io/v2/onboard/image/64ee65d1e2f6a8000142c687/web",
"recipientKeys": [
"6wobAAgEaSWgGYGB4uTpTfkDwnyXcZRX61JZNRZEx5ME"
]
},
"invitationUrl": "https://cloudagent.igrant.io/v1/64ec561de2f6a8000142c671/agent/?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiN2I0ZTY2NTgtMTQ4OS00YjJkLTk3MTYtZjVhYzg3MmRhOTVkIiwgImxhYmVsIjogIkphY29ic29ucyBsdW1iZXIgeWFyZCIsICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cHM6Ly9jbG91ZGFnZW50LmlncmFudC5pby92MS82NGVjNTYxZGUyZjZhODAwMDE0MmM2NzEvYWdlbnQvIiwgImltYWdlVXJsIjogImh0dHBzOi8vc3RhZ2luZy1hcGkuaWdyYW50LmlvL3YyL29uYm9hcmQvaW1hZ2UvNjRlZTY1ZDFlMmY2YTgwMDAxNDJjNjg3L3dlYiIsICJyZWNpcGllbnRLZXlzIjogWyJBbkxoUDRURVJ0eWFieWp5RHV1cXJtNGdiSGVrUENrS1NIRGdrMlhubmZXeiJdfQ=="
},
"firebaseDynamicLink": "https://datawallet.page.link/cncod1Qu52vzR3bU7"
}
connection_record = response['connection']
url = f"{DATA_MARKETPLACE_DW_URL}/v2/connections/create-invitation?multi_use=false&auto_accept=true"
authorization_header = DATA_MARKETPLACE_APIKEY

try:
response = requests.post(
url, headers={"Authorization": authorization_header}
)
response.raise_for_status()
response = response.json()
except requests.exceptions.RequestException as e:
return JsonResponse(
{"error": f"Error calling digital wallet: {str(e)}"},
status=status.HTTP_400_BAD_REQUEST,
)

connection_id = response.get("connection_id")

url = f"{DATA_MARKETPLACE_DW_URL}/v1/connections/{connection_id}/invitation/firebase"
try:
create_firebase_dynamic_link_response = requests.post(
url, headers={"Authorization": authorization_header}
)
create_firebase_dynamic_link_response.raise_for_status()
create_firebase_dynamic_link_response = (
create_firebase_dynamic_link_response.json()
)
except requests.exceptions.RequestException as e:
return JsonResponse(
{"error": f"Error creating Firebase dynamic link: {str(e)}"},
status=status.HTTP_400_BAD_REQUEST,
)

firebase_dynamic_link = create_firebase_dynamic_link_response.get(
"firebase_dynamic_link"
)

try:
connection = Connection.objects.get(dataSourceId=datasource)
Expand All @@ -54,10 +73,30 @@ def post(self, request):
dataSourceId=datasource,
connectionId=connection_id,
connectionState="invitation",
connectionRecord={}
connectionRecord={},
)

return JsonResponse(response)
connection_response_data = {
"connectionId": connection_id,
"invitation": {
"@type": response.get("invitation", {}).get("@type"),
"@id": response.get("invitation", {}).get("@id"),
"serviceEndpoint": response.get("invitation", {}).get(
"serviceEndpoint"
),
"label": response.get("invitation", {}).get("label"),
"imageUrl": response.get("invitation", {}).get("imageUrl"),
"recipientKeys": response.get("invitation", {}).get("recipientKeys"),
},
"invitationUrl": response.get("invitation_url"),
}

create_connection_response = {
"connection": connection_response_data,
"firebaseDynamicLink": firebase_dynamic_link,
}

return JsonResponse(create_connection_response)


class DISPConnectionsView(APIView):
Expand All @@ -68,32 +107,30 @@ def get(self, request):
try:
datasource = DataSource.objects.get(admin=request.user)
except DataSource.DoesNotExist:
return JsonResponse({'error': 'Data source not found'}, status=status.HTTP_400_BAD_REQUEST)
return JsonResponse(
{"error": "Data source not found"}, status=status.HTTP_400_BAD_REQUEST
)

try:
connections = Connection.objects.filter(dataSourceId=datasource)
connections, pagination_data = paginate_queryset(
connections, request)
connections, pagination_data = paginate_queryset(connections, request)
serializer = DISPConnectionSerializer(connections, many=True)
connection_data = serializer.data

except Connection.DoesNotExist:
# If no connection exists, return empty data
connection_data = None
pagination_data = {
'currentPage': 0,
'totalItems': 0,
'totalPages': 0,
'limit': 0,
'hasPrevious': False,
'hasNext': False
"currentPage": 0,
"totalItems": 0,
"totalPages": 0,
"limit": 0,
"hasPrevious": False,
"hasNext": False,
}

# Construct the response data
response_data = {
'connections': connection_data,
'pagination': pagination_data
}
response_data = {"connections": connection_data, "pagination": pagination_data}

return JsonResponse(response_data)

Expand All @@ -106,13 +143,19 @@ def delete(self, request, connectionId):
try:
datasource = DataSource.objects.get(admin=request.user)
except DataSource.DoesNotExist:
return JsonResponse({'error': 'Data source not found'}, status=status.HTTP_400_BAD_REQUEST)
return JsonResponse(
{"error": "Data source not found"}, status=status.HTTP_400_BAD_REQUEST
)

try:
connection = Connection.objects.get(
pk=connectionId, dataSourceId=datasource)
pk=connectionId, dataSourceId=datasource
)
connection.delete()
return JsonResponse({}, status=status.HTTP_204_NO_CONTENT)
except Connection.DoesNotExist:
# If no connection exists, return error
return JsonResponse({'error': 'Data source connection not found'}, status=status.HTTP_400_BAD_REQUEST)
return JsonResponse(
{"error": "Data source connection not found"},
status=status.HTTP_400_BAD_REQUEST,
)
21 changes: 21 additions & 0 deletions data_disclosure_agreement/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from data_disclosure_agreement.models import DataDisclosureAgreement


def query_ddas_and_update_is_latest_flag_to_false_for_previous_versions(
sender, instance, **kwargs
):
if instance.isLatestVersion:
ddas = DataDisclosureAgreement.objects.filter(
templateId=instance.templateId, isLatestVersion=True
).exclude(pk=instance.id)
for dda in ddas:
dda.isLatestVersion = False
dda.save()


post_save.connect(
query_ddas_and_update_is_latest_flag_to_false_for_previous_versions,
DataDisclosureAgreement,
)
3 changes: 3 additions & 0 deletions dataspace_backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,6 @@


STATIC_ROOT = "staticfiles"

DATA_MARKETPLACE_DW_URL = "https://cloudagent.igrant.io/v1/6609f0860c7d8c0001aea929/admin"
DATA_MARKETPLACE_APIKEY = "ApiKey eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiI2NjA5ZjA3MzBjN2Q4YzAwMDFhZWE5MjgiLCJvcmdpZCI6IiIsImVudiI6IiIsImV4cCI6MTc0MzAzMTYyMX0.dPGCINiGTjP7c9Fu_IuZEEd8zqmHBQdNjDahOONq2Ho"
1 change: 1 addition & 0 deletions webhook/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
urlpatterns = [
path('topic/connections/',views.receive_invitation),
path('topic/present_proof/', views.verify_certificate),
path('topic/published_data_disclosure_agreement/',views.receive_data_disclosure_agreement),
]
77 changes: 66 additions & 11 deletions webhook/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,28 @@
from rest_framework import status
from django.views.decorators.csrf import csrf_exempt
import json
from data_disclosure_agreement.models import DataDisclosureAgreement
from django.db.models.signals import post_save
from data_disclosure_agreement.signals import (
query_ddas_and_update_is_latest_flag_to_false_for_previous_versions,
)


# Create your views here.
@csrf_exempt
@require_POST
def verify_certificate(request):
response = request.body
response = json.loads(response)
presentation_exchange_id = response["data"]["presentation"]["presentationExchangeId"]
presentation_exchange_id = response["data"]["presentation"][
"presentationExchangeId"
]
presentation_state = response["data"]["presentation"]["state"]
presentation_record = response["data"]["presentation"]
try:
verification = Verification.objects.get(
presentationExchangeId=presentation_exchange_id)
presentationExchangeId=presentation_exchange_id
)
except Verification.DoesNotExist:
verification = None

Expand All @@ -29,29 +38,75 @@ def verify_certificate(request):

return HttpResponse(status=status.HTTP_200_OK)


@csrf_exempt
@require_POST
def receive_invitation(request):

response = request.body
response = json.loads(response)
connection_id = response["data"]["connection"]["connectionId"]
connection_state = response["data"]["connection"]["connectionState"]
connection_data = response["data"]["connection"]
connection_data.pop("id", None)
connection_id = response["connection_id"]
connection_state = response["state"]
connection_data = response

try:
connection = Connection.objects.get(
connectionId=connection_id)
connection = Connection.objects.get(connectionId=connection_id)
except Connection.DoesNotExist:
connection = None

if connection:
if connection.state != "active":
if connection.connectionState != "active":
connection.connectionState = connection_state
connection.connectionRecord = connection_data
connection.save()

return HttpResponse(status=status.HTTP_200_OK)


@csrf_exempt
@require_POST
def receive_data_disclosure_agreement(request):

response = request.body
response = json.loads(response)
connection_id = response["connection_id"]
dda_version = response["dda"]["version"]
dda_template_id = response["template_id"]

dda_connection = {"invitationUrl": response["connection_url"]}

try:
connection = Connection.objects.get(connectionId=connection_id)
except Connection.DoesNotExist:
connection = None

if connection:
data_disclosure_agreement = {
"language": response["dda"]["language"],
"version": response["dda"]["version"],
"templateId": dda_template_id,
"dataController": response["dda"]["dataController"],
"agreementPeriod": response["dda"]["agreementPeriod"],
"dataSharingRestrictions": response["dda"]["dataSharingRestrictions"],
"purpose": response["dda"]["purpose"],
"purposeDescription": response["dda"]["purposeDescription"],
"lawfulBasis": response["dda"]["lawfulBasis"],
"personalData": response["dda"]["personalData"],
"codeOfConduct": response["dda"]["codeOfConduct"],
"connection": dda_connection,
"status": "unlisted",
}
post_save.connect(
query_ddas_and_update_is_latest_flag_to_false_for_previous_versions,
DataDisclosureAgreement,
)

dda = DataDisclosureAgreement.objects.create(
version=dda_version,
templateId=dda_template_id,
dataSourceId=connection.dataSourceId,
dataDisclosureAgreementRecord=data_disclosure_agreement,
)
dda.save()

return HttpResponse(status=status.HTTP_200_OK)

0 comments on commit b5b7c92

Please sign in to comment.