import { cloneDeep , get , orderBy } from 'lodash' ; import { injectable } from 'tsyringe' ; import { ArianeeGateWayAuthentification } from '../../../../models/ArianeeGateWayAuthentification' ; import { ArianeeTokenId } from '../../../../models/ArianeeTokenId' ; import { ArianeeCertificatei18n , ArianeeCertificatei18nV3 } from '../../../../models/jsonSchema/certificates/ArianeeProducti18n' ; import { StoreNamespace } from '../../../../models/storeNamespace' ; import { ArianeeHttpClient } from '../../../libs/arianeeHttpClient/arianeeHttpClient' ; import { certificateParentMerger } from '../../../libs/certificateParentMerger/certificateParentMerger' ; import { isSchemai18n } from '../../../libs/is18n/certificateVersion' ; import { deepFreeze , replaceLanguageContentWithFavUserLanguage } from '../../../libs' ; import { hasParentCertificate } from '../../../libs/hasParentCertificate/hasParentCertificates' ; import { SimpleStore } from '../../../libs/simpleStore/simpleStore' ; import { CertificateSummaryBuilder } from '../../certificateSummary' ; import { CertificateContentContainer , ConsolidatedCertificateRequest , ConsolidatedIssuerRequestInterface } from '../../certificateSummary/certificateSummary' ; import { ArianeeAuthentificationService } from '../arianeeAuthentificationService/arianeeAuthentificationService' ; import { ConfigurationService } from '../configurationService/configurationService' ; import { ContractService } from '../contractService/contractsService' ; import { GlobalConfigurationService } from '../globalConfigurationService/globalConfigurationService' ; import { IdentityService } from '../identityService/identityService' ; import { UtilsService } from '../utilService/utilsService' ; import { WalletService } from '../walletService/walletService' ; @ injectable ( ) export class CertificateDetails { constructor ( private identityService , private httpClient , private contractService , private configurationService , private walletService , private utils , private store , private arianeeAuthentificationService , private globalConfigurationService ) { } public getCertificateIssuer = async ( parameters ) => { return this . fetchCertificateIssuer ( parameters ) ; } ; public fetchCertificateIssuer = async ( parameters ) => { const { certificateId } = parameters ; const issuerOf = ( ) => this . contractService . smartAssetContract . methods . issuerOf ( certificateId . toString ( ) ) . call ( ) ; const address = await this . store . get < string > ( StoreNamespace . certificateIssuer , certificateId , issuerOf ) ; return this . identityService . getIdentity ( { ... parameters , address } ) ; } ; public getCertificateOwner = async ( certificateId , certificateBuilder ? ) => { const owner = await this . contractService . smartAssetContract . methods . ownerOf ( certificateId . toString ( ) ) . call ( ) ; if ( certificateBuilder ) certificateBuilder . setOwner ( owner , this . walletService . address ) ; return owner ; } ; private getCertificateContentFromHttp = async ( certificateURI ) => { return this . httpClient . fetch ( certificateURI ) ; } ; private updateCertificateContentReadRPC = async ( parameters ) => { const { rpcEndPoint , arianeeRPCAuthentification , certificateId } = parameters ; return this . httpClient . RPCCall < CertificateContentContainer > ( rpcEndPoint , 'update.read' , { certificateId : certificateId , authentification : arianeeRPCAuthentification } ) ; } ; private originalCertificateContentReadRPC = async ( parameters ) => { const { rpcEndPoint , arianeeRPCAuthentification , certificateId } = parameters ; return this . httpClient . RPCCall < CertificateContentContainer > ( rpcEndPoint , 'certificate.read' , { certificateId : certificateId , authentification : arianeeRPCAuthentification } ) ; } ; private getCertificateContentFromRPC = async ( parameters ) => { const { certificateId , query , arianeeRPCAuthentification } = parameters ; const issuer = query . issuer as ConsolidatedIssuerRequestInterface ; let rpcEndPoint ; if ( issuer . rpcURI ) { rpcEndPoint = issuer . rpcURI ; } else { const address = await this . contractService . smartAssetContract . methods . issuerOf ( certificateId ) . call ( ) ; const identity = await this . identityService . getIdentity ( { ... parameters , address } ) . then ( d => { if ( d . data === undefined ) { console . warn ( `# ${ parameters . certificateURI } # failing to retrieve identity` ) ; } ; return d ; } ) ; rpcEndPoint = identity . data . rpcEndpoint ; } const rpcConfig = { certificateId , arianeeRPCAuthentification , rpcEndPoint } ; const certificateRPCResult = await this . updateCertificateContentReadRPC ( rpcConfig ) . catch ( error => { return this . originalCertificateContentReadRPC ( rpcConfig ) ; } ) ; return certificateRPCResult . result ; } ; private getContent = ( parameters ) : Promise < ArianeeCertificatei18n | undefined > => { const { certificateURI , certificateId } = parameters ; return this . getCertificateContentFromRPC ( parameters ) . catch ( err => { console . warn ( `# ${ certificateId } # Impossible to fetch content from RPC server` ) ; console . warn ( `# ${ certificateId } # Fallback to simple http call ${ certificateURI }` ) ; return this . getCertificateContentFromHttp ( parameters . certificateURI ) ; } ) . catch ( d => console . warn ( `# ${ certificateId } # Impossible to fetch content of this certificate ${ parameters . certificateId }` ) ) ; } ; public getCertificateContent = ( parameters ) : Promise < CertificateContentContainer > => { const { certificateId , query } = parameters ; const { content } = this . globalConfigurationService . getMergedQuery ( query ) ; const { forceRefresh } = content as ConsolidatedIssuerRequestInterface ; return this . store . get < CertificateContentContainer > ( StoreNamespace . certificateContent , certificateId , ( ) => this . getCertificateAndParentCertificate ( parameters ) , forceRefresh ) ; } ; public getCertificateAndParentCertificate = async ( parameters ) : Promise < CertificateContentContainer > => { const { query } = parameters ; const certificateContentSummary = await this . fetchCertificateContent ( parameters ) ; if ( certificateContentSummary . raw && hasParentCertificate ( certificateContentSummary . raw ) ) { const certificateContentSummaryWithParents = certificateContentSummary . raw as ArianeeCertificatei18nV3 ; const parentCertificates = certificateContentSummaryWithParents . parentCertificates ; const sortedParentLinks = orderBy ( parentCertificates , [ 'type' ] , [ 'asc' ] ) ; const parentLinks = sortedParentLinks . map ( d => d . arianeeLink ) . map ( arianeeLink => this . arianeeAuthentificationService . extractParametersFromArianeeLink ( arianeeLink ) ) ; const $fetchingParents = parentLinks . map ( link => { return this . getCertificateContent ( { certificateId : link . certificateId , passphrase : link . authentification , query } ) ; } ) ; const parentCertificateSummary = await Promise . all ( $fetchingParents ) ; const parentCertificateContent = parentCertificateSummary . map ( summary => summary . data ) ; const parentAuthenticity = parentCertificateSummary . map ( d => d . isAuthentic ) ; const isAuthentic = ! [ certificateContentSummary . isAuthentic , ... parentAuthenticity ] . includes ( false ) ; return { ... certificateContentSummary , isAuthentic , parents : parentCertificateSummary , isRawAuthentic : certificateContentSummary . isAuthentic , data : certificateParentMerger ( [ ... parentCertificateContent , certificateContentSummary . data ] ) } ; } return certificateContentSummary ; } ; private fetchCertificateContent = async ( parameters ) : Promise < CertificateContentContainer > => { if ( parameters . passphrase === undefined && get ( parameters , 'query.advanced.arianeeProofToken' ) ) { parameters . passphrase = parameters . query . advanced . arianeeProofToken ; } const { certificateId , passphrase , query } = parameters ; const tokenURI = await this . contractService . smartAssetContract . methods . tokenURI ( certificateId . toString ( ) ) . call ( ) ; const certificateContentData = await this . getContent ( { ... parameters , arianeeRPCAuthentification : await this . arianeeAuthentificationService . generateAuthentificationHeader ( certificateId , passphrase ) , certificateURI : tokenURI } ) ; if ( ! certificateContentData ) { return { imprint : undefined , data : undefined , isRawAuthentic : false , isAuthentic : false , raw : undefined } ; } const certificateSchema = await this . httpClient . fetch ( certificateContentData . $schema ) ; const hash = await this . utils . cert ( certificateSchema , certificateContentData ) ; let tokenImprint ; if ( this . contractService . updateSmartAssetContract ) { tokenImprint = await this . contractService . updateSmartAssetContract . methods . getImprint ( certificateId . toString ( ) ) . call ( ) ; } else { tokenImprint = await this . contractService . smartAssetContract . methods . tokenImprint ( certificateId . toString ( ) ) . call ( ) ; } const isCertificateContentValid = hash === tokenImprint ; const certificateSummary = { imprint : tokenImprint , data : certificateContentData , isRawAuthentic : isCertificateContentValid , isAuthentic : isCertificateContentValid , raw : deepFreeze ( cloneDeep ( certificateContentData ) ) } ; if ( get ( query , 'advanced.languages' ) && get ( certificateSummary , 'data' ) && isSchemai18n ( certificateContentData ) ) { certificateSummary . data = replaceLanguageContentWithFavUserLanguage ( certificateContentData , query . advanced . languages ) as any ; } if ( get ( certificateSummary , 'data' ) && get ( certificateSummary , 'data.externalContents' ) ) { const externalContents = get ( certificateSummary , 'data.externalContents' ) ; externalContents . sort ( function ( a , b ) { return a . order - b . order ; } ) ; certificateSummary . data . externalContents = externalContents ; } if ( get ( certificateSummary , 'data' ) && get ( certificateSummary , 'data.medias' ) ) { const medias = get ( certificateSummary , 'data.medias' ) ; medias . sort ( function ( a , b ) { return a . order - b . order ; } ) ; certificateSummary . data . medias = medias ; } return certificateSummary ; } ; }