From a85c1135fb301b70c01305902ceef328fb1de6ee Mon Sep 17 00:00:00 2001 From: Bertrand Date: Mon, 18 Mar 2024 12:20:53 +0100 Subject: [PATCH 1/8] Add new types in schema --- schema.graphql | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/schema.graphql b/schema.graphql index 335e4d9b..e50dbe78 100644 --- a/schema.graphql +++ b/schema.graphql @@ -120,6 +120,7 @@ type UserDescription @entity(immutable: true) { video_url: String #url image_url: String #url web3mailPreferences: UserWeb3mailPreferences + credentials: [Credential!] @derivedFrom(field: "userDescription") } type UserWeb3mailPreferences @entity(immutable: true) { @@ -133,6 +134,44 @@ type UserWeb3mailPreferences @entity(immutable: true) { activeOnProtocolMarketing: Boolean } +type Credential @entity(immutable: true) { + id: ID! + issuer: String + signature1: String + signature2: String + credentialDetail: CredentialDetail + userDescription: UserDescription! +} + +type CredentialDetail @entity(immutable: true) { + id: ID! + author: String + platform: String + description: String + issueTime: String + expiryTime: String + userAddress: String + claims: [Claim!] + claimsEncrypted: ClaimsEncrypted +} + +type Claim @entity(immutable: true) { + id: ID! + platform: String + criteria: String + condition: String + value: String + credentialDetail: CredentialDetail! +} + +type ClaimsEncrypted @entity(immutable: true) { + id: ID! + total: BigInt + ciphertext: String + dataToEncryptHash: String + condition: String +} + enum ProposalStatus { Pending Validated From 47d39db706eae8d638a380954d31a00dece7af7c Mon Sep 17 00:00:00 2001 From: Bertrand Date: Tue, 19 Mar 2024 12:22:04 +0100 Subject: [PATCH 2/8] Index the new types in IPFS + add parsing --- src/mappings/ipfs-data.ts | 88 +++++++++++++++++++++++++++++++++++++++ subgraph.yaml | 4 ++ 2 files changed, 92 insertions(+) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index b6144995..98e7d199 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -7,6 +7,10 @@ import { ReviewDescription, UserWeb3mailPreferences, EvidenceDescription, + Credential, + CredentialDetail, + Claim, + ClaimsEncrypted, } from '../../generated/schema' import { getOrCreateKeyword } from '../getters' @@ -155,6 +159,80 @@ export function handleUserData(content: Bytes): void { description.web3mailPreferences = id } + const credentialsArray = getValueAsArray(jsonObject, 'credentials') + if (credentialsArray) { + for (let i = 0; i < credentialsArray.length; i++) { + const credentialObj = credentialsArray[i].toObject() + const credentialId = getValueAsString(credentialObj, 'id') + + if (credentialId !== null) { + let credential = new Credential(credentialId) + // Fill Credential fields + credential.issuer = getValueAsString(credentialObj, 'issuer') + credential.signature1 = getValueAsString(credentialObj, 'signature1') + credential.signature2 = getValueAsString(credentialObj, 'signature2') + credential.userDescription = id + + // Handle CredentialDetail + const credentialDetailObj = getValueAsObject(credentialObj, 'credential') + if (credentialDetailObj) { + const credentialDetailId = getValueAsString(credentialDetailObj, 'id') + if (credentialDetailId !== null) { + let credentialDetail = new CredentialDetail(credentialDetailId) + // Fill CredentialDetail fields + credentialDetail.author = getValueAsString(credentialDetailObj, 'author') + credentialDetail.platform = getValueAsString(credentialDetailObj, 'platform') + credentialDetail.description = getValueAsString(credentialDetailObj, 'description') + credentialDetail.issueTime = getValueAsString(credentialDetailObj, 'issueTime') + credentialDetail.expiryTime = getValueAsString(credentialDetailObj, 'expiryTime') + credentialDetail.userAddress = getValueAsString(credentialDetailObj, 'userAddress') + + // Handle Claims array within CredentialDetail + const claimsArray = getValueAsArray(credentialDetailObj, 'claims') + if (claimsArray) { + for (let i = 0; i < claimsArray.length; i++) { + const claimObj = claimsArray[i].toObject() + const claimId = getValueAsString(claimObj, 'id') + + if (claimId !== null) { + let claim = new Claim(claimId) + claim.platform = getValueAsString(claimObj, 'platform') + claim.criteria = getValueAsString(claimObj, 'criteria') + claim.condition = getValueAsString(claimObj, 'condition') + claim.value = getValueAsString(claimObj, 'value') + claim.credentialDetail = credentialDetailId + + claim.save() + } + } + } + + // Handle ClaimsEncrypted object within CredentialDetail + const claimsEncryptedObj = getValueAsObject(credentialDetailObj, 'claimsEncrypted') + if (claimsEncryptedObj) { + const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') + if (claimsEncryptedId !== null) { + let claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) + claimsEncrypted.ciphertext = getValueAsString(claimsEncryptedObj, 'ciphertext') + claimsEncrypted.dataToEncryptHash = getValueAsString(claimsEncryptedObj, 'dataToEncryptHash') + claimsEncrypted.total = getValueAsBigInt(claimsEncryptedObj, 'total'); + claimsEncrypted.condition = getValueAsString(claimsEncryptedObj, 'condition') + + claimsEncrypted.save() + credentialDetail.claimsEncrypted = claimsEncryptedId + } + } + + credentialDetail.save() + credential.credentialDetail = credentialDetailId + } + } + + credential.save() + } + } + } + //Creates duplicate values. Open issue //https://github.com/graphprotocol/graph-node/issues/4087 //description.skills = createKeywordEntities(description.skills_raw!)! @@ -255,6 +333,16 @@ function getValueAsBoolean(jsonObject: TypedMap, key: string, return value.toBool() } +function getValueAsArray(jsonObject: TypedMap, key: string): JSONValue[] | null { + const value = jsonObject.get(key) + + if (value == null || value.isNull() || value.kind != JSONValueKind.ARRAY) { + return null + } + + return value.toArray() +} + //Transforms a comma separated string of keywords into an Array of Keyword.id entities. function createKeywordEntities(keywords: string): string[] | null { const _keywords = keywords.split(',') diff --git a/subgraph.yaml b/subgraph.yaml index 7b8cd804..14c07faa 100644 --- a/subgraph.yaml +++ b/subgraph.yaml @@ -264,6 +264,10 @@ templates: - UserDescription - UserWeb3mailPreferences - Keyword + - Credential + - CredentialDetail + - ClaimsEncrypted + - Claim abis: - name: TalentLayerID file: ./abis/TalentLayerID.json From 022635828b6270375f2994ddc08ac90d4dc9040a Mon Sep 17 00:00:00 2001 From: Bertrand Date: Tue, 2 Apr 2024 15:14:43 +0200 Subject: [PATCH 3/8] add exception --- src/mappings/ipfs-data.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index 98e7d199..f6ce9d52 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -164,9 +164,12 @@ export function handleUserData(content: Bytes): void { for (let i = 0; i < credentialsArray.length; i++) { const credentialObj = credentialsArray[i].toObject() const credentialId = getValueAsString(credentialObj, 'id') - + if (credentialId !== null) { - let credential = new Credential(credentialId) + let credential = Credential.load(credentialId) + if (credential == null) { + credential = new Credential(credentialId) + } // Fill Credential fields credential.issuer = getValueAsString(credentialObj, 'issuer') credential.signature1 = getValueAsString(credentialObj, 'signature1') @@ -178,7 +181,10 @@ export function handleUserData(content: Bytes): void { if (credentialDetailObj) { const credentialDetailId = getValueAsString(credentialDetailObj, 'id') if (credentialDetailId !== null) { - let credentialDetail = new CredentialDetail(credentialDetailId) + let credentialDetail = CredentialDetail.load(credentialDetailId) + if (credentialDetail == null) { + credentialDetail = new CredentialDetail(credentialDetailId) + } // Fill CredentialDetail fields credentialDetail.author = getValueAsString(credentialDetailObj, 'author') credentialDetail.platform = getValueAsString(credentialDetailObj, 'platform') @@ -195,7 +201,10 @@ export function handleUserData(content: Bytes): void { const claimId = getValueAsString(claimObj, 'id') if (claimId !== null) { - let claim = new Claim(claimId) + let claim = Claim.load(claimId) + if (claim == null) { + claim = new Claim(claimId) + } claim.platform = getValueAsString(claimObj, 'platform') claim.criteria = getValueAsString(claimObj, 'criteria') claim.condition = getValueAsString(claimObj, 'condition') @@ -212,12 +221,15 @@ export function handleUserData(content: Bytes): void { if (claimsEncryptedObj) { const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') if (claimsEncryptedId !== null) { - let claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) + let claimsEncrypted = ClaimsEncrypted.load(claimsEncryptedId) + if (claimsEncrypted == null) { + claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) + } claimsEncrypted.ciphertext = getValueAsString(claimsEncryptedObj, 'ciphertext') claimsEncrypted.dataToEncryptHash = getValueAsString(claimsEncryptedObj, 'dataToEncryptHash') claimsEncrypted.total = getValueAsBigInt(claimsEncryptedObj, 'total'); claimsEncrypted.condition = getValueAsString(claimsEncryptedObj, 'condition') - + claimsEncrypted.save() credentialDetail.claimsEncrypted = claimsEncryptedId } From 917fc7fdf45fbf6b6ccfa5c496f9b0df9410c39f Mon Sep 17 00:00:00 2001 From: Bertrand Date: Tue, 2 Apr 2024 15:31:39 +0200 Subject: [PATCH 4/8] add logs --- src/mappings/ipfs-data.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index f6ce9d52..d791e45c 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -181,6 +181,7 @@ export function handleUserData(content: Bytes): void { if (credentialDetailObj) { const credentialDetailId = getValueAsString(credentialDetailObj, 'id') if (credentialDetailId !== null) { + log.debug('credentialDetailId = {}', [credentialDetailId]) let credentialDetail = CredentialDetail.load(credentialDetailId) if (credentialDetail == null) { credentialDetail = new CredentialDetail(credentialDetailId) @@ -201,6 +202,7 @@ export function handleUserData(content: Bytes): void { const claimId = getValueAsString(claimObj, 'id') if (claimId !== null) { + log.debug('claimId = {}', [claimId]) let claim = Claim.load(claimId) if (claim == null) { claim = new Claim(claimId) @@ -221,6 +223,7 @@ export function handleUserData(content: Bytes): void { if (claimsEncryptedObj) { const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') if (claimsEncryptedId !== null) { + log.debug('claimsEncryptedId = {}', [claimsEncryptedId]) let claimsEncrypted = ClaimsEncrypted.load(claimsEncryptedId) if (claimsEncrypted == null) { claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) From ecada53b6ac236aa7ee01ad47af2c73f38efdaf6 Mon Sep 17 00:00:00 2001 From: Bertrand Date: Wed, 3 Apr 2024 11:14:29 +0200 Subject: [PATCH 5/8] replace log by warning for easier debug in thegraph --- src/mappings/ipfs-data.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index d791e45c..6dadb64b 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -166,6 +166,7 @@ export function handleUserData(content: Bytes): void { const credentialId = getValueAsString(credentialObj, 'id') if (credentialId !== null) { + log.warning('credentialId = {}', [credentialId.toString()]) let credential = Credential.load(credentialId) if (credential == null) { credential = new Credential(credentialId) @@ -181,7 +182,7 @@ export function handleUserData(content: Bytes): void { if (credentialDetailObj) { const credentialDetailId = getValueAsString(credentialDetailObj, 'id') if (credentialDetailId !== null) { - log.debug('credentialDetailId = {}', [credentialDetailId]) + log.warning('credentialDetailId = {}', [credentialDetailId.toString()]) let credentialDetail = CredentialDetail.load(credentialDetailId) if (credentialDetail == null) { credentialDetail = new CredentialDetail(credentialDetailId) @@ -202,7 +203,7 @@ export function handleUserData(content: Bytes): void { const claimId = getValueAsString(claimObj, 'id') if (claimId !== null) { - log.debug('claimId = {}', [claimId]) + log.warning('claimId = {}', [claimId.toString()]) let claim = Claim.load(claimId) if (claim == null) { claim = new Claim(claimId) @@ -223,7 +224,7 @@ export function handleUserData(content: Bytes): void { if (claimsEncryptedObj) { const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') if (claimsEncryptedId !== null) { - log.debug('claimsEncryptedId = {}', [claimsEncryptedId]) + log.warning('claimsEncryptedId = {}', [claimsEncryptedId.toString()]) let claimsEncrypted = ClaimsEncrypted.load(claimsEncryptedId) if (claimsEncrypted == null) { claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) From 3e0745eb1f9986ea85aeff902c704b2d3c0ef528 Mon Sep 17 00:00:00 2001 From: Bertrand Date: Mon, 8 Apr 2024 15:50:55 +0200 Subject: [PATCH 6/8] replace !== null by the correct assemblyscript notation --- src/mappings/ipfs-data.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index 6dadb64b..50d390b3 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -37,7 +37,7 @@ export function handleServiceData(content: Bytes): void { description.startDate = getValueAsBigInt(jsonObject, 'startDate') description.expectedEndDate = getValueAsBigInt(jsonObject, 'expectedEndDate') const keywords = getValueAsString(jsonObject, 'keywords') - if (keywords !== null) { + if (keywords) { description.keywords_raw = keywords.toLowerCase() } description.rateToken = getValueAsString(jsonObject, 'rateToken') @@ -121,7 +121,7 @@ export function handleUserData(content: Bytes): void { description.title = getValueAsString(jsonObject, 'title') description.about = getValueAsString(jsonObject, 'about') const skills = getValueAsString(jsonObject, 'skills') - if (skills !== null) { + if (skills) { description.skills_raw = skills.toLowerCase() } description.timezone = getValueAsBigInt(jsonObject, 'timezone') @@ -165,7 +165,7 @@ export function handleUserData(content: Bytes): void { const credentialObj = credentialsArray[i].toObject() const credentialId = getValueAsString(credentialObj, 'id') - if (credentialId !== null) { + if (credentialId) { log.warning('credentialId = {}', [credentialId.toString()]) let credential = Credential.load(credentialId) if (credential == null) { @@ -181,7 +181,7 @@ export function handleUserData(content: Bytes): void { const credentialDetailObj = getValueAsObject(credentialObj, 'credential') if (credentialDetailObj) { const credentialDetailId = getValueAsString(credentialDetailObj, 'id') - if (credentialDetailId !== null) { + if (credentialDetailId) { log.warning('credentialDetailId = {}', [credentialDetailId.toString()]) let credentialDetail = CredentialDetail.load(credentialDetailId) if (credentialDetail == null) { @@ -202,7 +202,7 @@ export function handleUserData(content: Bytes): void { const claimObj = claimsArray[i].toObject() const claimId = getValueAsString(claimObj, 'id') - if (claimId !== null) { + if (claimId) { log.warning('claimId = {}', [claimId.toString()]) let claim = Claim.load(claimId) if (claim == null) { @@ -223,7 +223,7 @@ export function handleUserData(content: Bytes): void { const claimsEncryptedObj = getValueAsObject(credentialDetailObj, 'claimsEncrypted') if (claimsEncryptedObj) { const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') - if (claimsEncryptedId !== null) { + if (claimsEncryptedId) { log.warning('claimsEncryptedId = {}', [claimsEncryptedId.toString()]) let claimsEncrypted = ClaimsEncrypted.load(claimsEncryptedId) if (claimsEncrypted == null) { From 6a375556877a0c6fc6a0b55da19fc0b8e42173ed Mon Sep 17 00:00:00 2001 From: Bertrand Date: Mon, 8 Apr 2024 21:33:55 +0200 Subject: [PATCH 7/8] Fix duplicate id error with timestamp --- src/mappings/ipfs-data.ts | 39 ++++++++++++--------------------- src/mappings/talent-layer-id.ts | 1 + 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/mappings/ipfs-data.ts b/src/mappings/ipfs-data.ts index 50d390b3..7919e8d0 100644 --- a/src/mappings/ipfs-data.ts +++ b/src/mappings/ipfs-data.ts @@ -114,6 +114,7 @@ export function handleUserData(content: Bytes): void { const context = dataSource.context() const userId = context.getBigInt('userId') const id = context.getString('id') + const timestamp = context.getString('timestamp') let description = new UserDescription(id) description.user = userId.toString() @@ -166,11 +167,8 @@ export function handleUserData(content: Bytes): void { const credentialId = getValueAsString(credentialObj, 'id') if (credentialId) { - log.warning('credentialId = {}', [credentialId.toString()]) - let credential = Credential.load(credentialId) - if (credential == null) { - credential = new Credential(credentialId) - } + const uniqId = credentialId + '-' + i.toString() + '-' + timestamp + let credential = new Credential(uniqId) // Fill Credential fields credential.issuer = getValueAsString(credentialObj, 'issuer') credential.signature1 = getValueAsString(credentialObj, 'signature1') @@ -182,11 +180,8 @@ export function handleUserData(content: Bytes): void { if (credentialDetailObj) { const credentialDetailId = getValueAsString(credentialDetailObj, 'id') if (credentialDetailId) { - log.warning('credentialDetailId = {}', [credentialDetailId.toString()]) - let credentialDetail = CredentialDetail.load(credentialDetailId) - if (credentialDetail == null) { - credentialDetail = new CredentialDetail(credentialDetailId) - } + const uniqCredentialDetailId = credentialDetailId + '-' + i.toString() + '-' + timestamp + let credentialDetail = new CredentialDetail(uniqCredentialDetailId) // Fill CredentialDetail fields credentialDetail.author = getValueAsString(credentialDetailObj, 'author') credentialDetail.platform = getValueAsString(credentialDetailObj, 'platform') @@ -198,21 +193,18 @@ export function handleUserData(content: Bytes): void { // Handle Claims array within CredentialDetail const claimsArray = getValueAsArray(credentialDetailObj, 'claims') if (claimsArray) { - for (let i = 0; i < claimsArray.length; i++) { - const claimObj = claimsArray[i].toObject() + for (let j = 0; j < claimsArray.length; j++) { + const claimObj = claimsArray[j].toObject() const claimId = getValueAsString(claimObj, 'id') if (claimId) { - log.warning('claimId = {}', [claimId.toString()]) - let claim = Claim.load(claimId) - if (claim == null) { - claim = new Claim(claimId) - } + const uniqClaimId = claimId + '-' + j.toString() + '-' + timestamp + let claim = new Claim(uniqClaimId) claim.platform = getValueAsString(claimObj, 'platform') claim.criteria = getValueAsString(claimObj, 'criteria') claim.condition = getValueAsString(claimObj, 'condition') claim.value = getValueAsString(claimObj, 'value') - claim.credentialDetail = credentialDetailId + claim.credentialDetail = uniqCredentialDetailId claim.save() } @@ -224,23 +216,20 @@ export function handleUserData(content: Bytes): void { if (claimsEncryptedObj) { const claimsEncryptedId = getValueAsString(claimsEncryptedObj, 'id') if (claimsEncryptedId) { - log.warning('claimsEncryptedId = {}', [claimsEncryptedId.toString()]) - let claimsEncrypted = ClaimsEncrypted.load(claimsEncryptedId) - if (claimsEncrypted == null) { - claimsEncrypted = new ClaimsEncrypted(claimsEncryptedId) - } + const uniqClaimsEncryptedId = claimsEncryptedId + '-' + i.toString() + '-' + timestamp + let claimsEncrypted = new ClaimsEncrypted(uniqClaimsEncryptedId) claimsEncrypted.ciphertext = getValueAsString(claimsEncryptedObj, 'ciphertext') claimsEncrypted.dataToEncryptHash = getValueAsString(claimsEncryptedObj, 'dataToEncryptHash') claimsEncrypted.total = getValueAsBigInt(claimsEncryptedObj, 'total'); claimsEncrypted.condition = getValueAsString(claimsEncryptedObj, 'condition') claimsEncrypted.save() - credentialDetail.claimsEncrypted = claimsEncryptedId + credentialDetail.claimsEncrypted = uniqClaimsEncryptedId } } credentialDetail.save() - credential.credentialDetail = credentialDetailId + credential.credentialDetail = uniqCredentialDetailId } } diff --git a/src/mappings/talent-layer-id.ts b/src/mappings/talent-layer-id.ts index ccefbe06..2ac17ae8 100644 --- a/src/mappings/talent-layer-id.ts +++ b/src/mappings/talent-layer-id.ts @@ -32,6 +32,7 @@ export function handleCidUpdated(event: CidUpdated): void { const context = new DataSourceContext() context.setBigInt('userId', userId) context.setString('id', dataId) + context.setString('timestamp', event.block.timestamp.toString()) UserData.createWithContext(newCid, context) user.save() From b89fd6514d0809febc807568bd2d502147a8a0fe Mon Sep 17 00:00:00 2001 From: Bertrand Date: Fri, 5 Apr 2024 14:34:16 +0200 Subject: [PATCH 8/8] fix the claim indexing for arrays --- schema.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema.graphql b/schema.graphql index e50dbe78..66e46cd3 100644 --- a/schema.graphql +++ b/schema.graphql @@ -151,7 +151,7 @@ type CredentialDetail @entity(immutable: true) { issueTime: String expiryTime: String userAddress: String - claims: [Claim!] + claims: [Claim!] @derivedFrom(field: "credentialDetail") claimsEncrypted: ClaimsEncrypted }