Skip to content

Commit

Permalink
Feat/DDO to Gaia-X Service Credential (#595)
Browse files Browse the repository at this point in the history
* feat: add download DDO button

* feat: added comments

* refactor: extract downloadJSON function, add JSON validation, and make download DDO button visible outside debug mode

* feat: created new JSON and added modal for data input

* feat: updated JSON, changed modal title, added delete function for credentials, and improved modal styling

* fix: resolved issue with button type attribute

* fix: fixed createServiceCredential function

* fix: format correctly

* feat: migrated form to Formik

* feat: added validation and placeholder

* refactor: updated typing,removed console.log, fix tooltip, cleaned up CSS,merged functions, adjusted validation, added button type, updated output JSON, and modified text fields

* refactor: replaced values with placeholders for gx:URL and gx:hash in JSON
  • Loading branch information
KevinRohlf authored Oct 15, 2024
1 parent 708a065 commit a29df2a
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 21 deletions.
45 changes: 45 additions & 0 deletions content/DDOtoServiceCredential/serviceCredentialForm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"metadata": {
"title": "Metadata",
"fields": [
{
"name": "didweb",
"label": "Service Provider DID:WEB ",
"placeholder": "did:web:yourdomain.eu",
"help": "The DID:WEB of the service provider / issuer publishing the Gaia-X Service Credential.",
"required": true
},
{
"name": "credentialHostingPath",
"label": "Service Credential Hosting Path ",
"placeholder": "https://yourdomain.eu/.well-known",
"help": "The base path to your public storage of Gaia-X Service Credentials.",
"required": true
},
{
"name": "pathToParticipantCredential",
"label": "Service Provider Participant Credential ",
"placeholder": "https://yourdomain.eu/.well-known/participant_vp.json",
"help": "URI of the service provider Gaia-X Participant Credential required to identity the responsible service provider.",
"required": true
},
{
"name": "knownDependencyCredentials",
"label": "Known Dependency Credentials",
"placeholder": "https://www.yourdomain.eu/.well-known/2210_gx_service_provider_1.json",
"help": "Contains resolvable links to the Gaia-X service credentials related to this service that can exist independently of it.",
"required": false
},
{
"name": "knownAggregatedServiceCredentials",
"label": "Known Aggregated Resource Credentials",
"placeholder": "https://www.yourdomain.eu/.well-known/2210_gx_resource_data_road_condition_7.json",
"help": "Contains resolveable links to Gaia-X Resource and Service Credentials related to this service that can exist independently of it.",
"required": false
}
]
},
"submission": {
"title": "Submit"
}
}
14 changes: 6 additions & 8 deletions src/@utils/downloadJSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ export function isValidJSON(json: string): boolean {
}
}

// Download JSON file
export function downloadJSON(json: string, filename: string): void {
// check if the json is valid
if (isValidJSON(json)) {
const element = document.createElement('a') // create an <a> tag
const element = document.createElement('a')
const file = new Blob([json], {
type: 'application/json'
}) // create a file
element.href = URL.createObjectURL(file) // create a URL for the file
element.download = `${filename}.json` // Set the file name
document.body.appendChild(element) // Append the tag to the body
element.click() // Click the a element
})
element.href = URL.createObjectURL(file)
element.download = `${filename}.json`
document.body.appendChild(element)
element.click()
}
}
24 changes: 24 additions & 0 deletions src/components/@shared/DDODownloadButton/_validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as Yup from 'yup'

export const initialValuesAsset = {
didweb: '',
credentialHostingPath: '',
pathToParticipantCredential: '',
knownDependencyCredentials: '',
knownAggregatedServiceCredentials: ''
}

export const validationAsset = Yup.object().shape({
didweb: Yup.string()
.matches(/^did:web:/, 'Invalid DID (only did:web is allowed)')
.required('Required'),
credentialHostingPath: Yup.string()
.url('Invalid URL')
.matches(/.*[^/]$/, 'URL must not end with /')
.required('Required'),
pathToParticipantCredential: Yup.string()
.url('Invalid URL')
.required('Required'),
knownDependencyCredentials: Yup.string().optional().url('Invalid URL'),
knownAggregatedServiceCredentials: Yup.string().optional().url('Invalid URL')
})
73 changes: 73 additions & 0 deletions src/components/@shared/DDODownloadButton/createJSON.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { downloadJSON } from '@utils/downloadJSON'

export interface DDOData {
id: string
services: { timeout: string }[]
}

interface FormData {
didweb: string
credentialHostingPath: string
pathToParticipantCredential: string
dependencyCredentialsList: { id: string }[]
serviceCredentialList: { id: string }[]
}

function getDomain(url: string) {
url = url.split('/').splice(0, 3).join('/')
return url
}

export function createServiceCredential(asset: DDOData, formData: FormData) {
const filename = `service_did_op_${asset.id.split(':')[2]}` // to get only the id without did:op:
const metadata = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3id.org/security/suites/jws-2020/v1',
'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#'
],
type: 'VerifiableCredential',
id: `${formData.credentialHostingPath}/${filename}.json`,
issuer: formData.didweb,
issuanceDate: '',
credentialSubject: {
id: asset.id,
type: 'gx:ServiceOffering',
'gx:providedBy': {
id: formData.pathToParticipantCredential
},
'gx:maintainedBy': {
id: formData.pathToParticipantCredential
},
'gx:serviceOffering:serviceModel': 'subscription',
'gx:serviceOffering:subscriptionDuration':
asset.services[0].timeout || 'unlimited',
'gx:policy': `${formData.credentialHostingPath}/yourpolicy.json`,
'gx:termsAndConditions': {
'gx:URL': '[basedomain]/yourtermsandconditions.txt',
'gx:hash': '[hash]'
},
'gx:dataAccountExport': {
'gx:requestType': 'email',
'gx:accessType': 'digital',
'gx:formatType': 'mime/json'
},
'gx:serviceOffering:dataProtectionRegime': ['GDPR2016'],
'gx:serviceOffering:gdpr': [
{
'gx:serviceOffering:imprint': `${getDomain(
formData.credentialHostingPath
)}/imprint/`
},
{
'gx:serviceOffering:privacyPolicy': `${getDomain(
formData.credentialHostingPath
)}/privacy/`
}
],
'gx:dependsOn': formData.dependencyCredentialsList || [],
'gx:aggregationOf': formData.serviceCredentialList || []
}
}
downloadJSON(JSON.stringify(metadata), filename)
}
18 changes: 18 additions & 0 deletions src/components/@shared/DDODownloadButton/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.credential_margin_bottom {
margin-bottom: calc(var(--spacer) * var(--line-height));
}
.credential_input {
width: 70%;
}

.credential_input_div {
display: flex;
justify-content: space-between;
align-items: flex-end;
flex-wrap: wrap;
width: 100%;
}

.button {
max-height: 70px;
}
122 changes: 111 additions & 11 deletions src/components/@shared/DDODownloadButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,120 @@
import { ReactElement } from 'react'
import { ReactElement, useState } from 'react'
import Button from '../atoms/Button'
import { downloadJSON } from '@utils/downloadJSON'
import Modal from '../atoms/Modal'
import Input from '../FormInput'
import { createServiceCredential, DDOData } from './createJSON'
import { Field, Form, Formik } from 'formik'
import { getFieldContent } from '@utils/form'
import content from '../../../../content/DDOtoServiceCredential/serviceCredentialForm.json'
import { initialValuesAsset, validationAsset } from './_validation'
import InputWithList from './inputWithList'
import styles from './index.module.css'

interface values {
didweb: string
credentialHostingPath: string
pathToParticipantCredential: string
knownDependencyCredentials: string
knownAggregatedServiceCredentials: string
}

export default function DDODownloadButton({
asset
}: {
asset: any
asset: DDOData
}): ReactElement {
const [openModal, setOpenModal] = useState(false)
const [serviceCredentialList, setServiceCredentialList] = useState<
{ id: string }[]
>([])
const [dependencyCredentialsList, setDependencyCredentialsList] = useState<
{ id: string }[]
>([])
const clearLists = () => {
setOpenModal(false)
setServiceCredentialList([])
setDependencyCredentialsList([])
}
const handleSubmit = (values: values) => {
const formData = {
...values,
serviceCredentialList,
dependencyCredentialsList
}
createServiceCredential(asset, formData)
clearLists()
}

return (
<Button
onClick={() =>
downloadJSON(JSON.stringify(asset), `${asset.metadata.name}_metadata`)
}
size="small"
>
Download DDO
</Button>
<>
<Button
className={styles.button}
onClick={() => setOpenModal(true)}
size="small"
>
Prepare Service Credential
</Button>

<Modal
title="Prepare Gaia-X Service Credential"
isOpen={openModal}
onToggleModal={() => {
clearLists()
}}
>
<p>
This manual export functionality will assist you to create Gaia-X
Service Credentials for this service which can be added to this
service for verification against the&nbsp;
<Button
style="text"
href={'https://docs.gaia-x.eu/framework/?tab=clearing-house'}
>
Gaia-X Digital Clearing Houses (GXDCH)
</Button>
. Credentials should be signed and hosted by the service provider.
</p>
<Formik
initialValues={initialValuesAsset}
validationSchema={validationAsset}
onSubmit={handleSubmit}
>
<Form>
<Field
{...getFieldContent('didweb', content.metadata.fields)}
component={Input}
name="didweb"
/>
<Field
{...getFieldContent(
'credentialHostingPath',
content.metadata.fields
)}
component={Input}
name="credentialHostingPath"
/>
<Field
{...getFieldContent(
'pathToParticipantCredential',
content.metadata.fields
)}
component={Input}
name="pathToParticipantCredential"
/>
<InputWithList
fieldname="knownDependencyCredentials"
setList={setDependencyCredentialsList}
list={dependencyCredentialsList}
/>
<InputWithList
fieldname="knownAggregatedServiceCredentials"
setList={setServiceCredentialList}
list={serviceCredentialList}
/>
<Button type="submit">Download</Button>
</Form>
</Formik>
</Modal>
</>
)
}
Loading

0 comments on commit a29df2a

Please sign in to comment.