@minespider/core-bundles
Version:
A high-level SDK for Minespider Core. It abstract the low-level features from the core SDK for a more high-level usage such as DAPPs. Some of the features are 1:1 with the SDK some others abstract some low-level interactions or multiple actions
756 lines (652 loc) • 19.8 kB
text/typescript
import * as ethers from "ethers";
import {
CertificateDTO,
CertificateEnvelope,
CertificateFile,
EntityType,
CertificateHistoryResponseEntryInterface,
Minespider,
FileMetadata,
MaterialMassBalanceCertification,
EntityDTO,
} from "@minespider/core-sdk";
import { AllowanceEntry } from "@minespider/core-sdk/dist/types/domain/queries/getMaterialAllowancesQuery";
import {
JSONInterface,
BaseMetadataInterface,
} from "@minespider/core-sdk/dist/types/service/metadata-service";
import { CertificateCacheServiceAdapterInterface, CertificateCacheServiceAdapter } from "../../Communication/Adapter/CertificateCacheServiceAdapter";
const ERROR_NO_PRIVATE_METADATA = "No private metadata found for certificate";
const ERROR_NO_PUBLIC_METADATA = "No public metadata found for certificate";
const ERROR_PATH_NOT_EXIST = "Path doesn't exist";
export interface ParsedCertificate extends CertificateDTO {
metadataFiles: {
public: FileMetadata[];
private: FileMetadata[];
};
}
const certificateEnvelopesMap = new Map<string, CertificateEnvelope>();
const certificates = new Map<string, ParsedCertificate>();
const entities = new Map<string, EntityDTO>();
export class MinespiderModel {
private cacheAdapter: CertificateCacheServiceAdapterInterface;
private wallet: ethers.Wallet;
constructor(
private minespider: Minespider,
mnemonic: string,
certificateCacheServiceEndpoint: string
) {
this.cacheAdapter = new CertificateCacheServiceAdapter(certificateCacheServiceEndpoint)
this.wallet = ethers.Wallet.fromMnemonic(mnemonic)
}
/**
* Retreive a single Certificate Data Envelope
*
* @param id
*/
public async getCertificateById(id: string): Promise<any> {
if (!certificates.has(id.toLowerCase())) {
try {
const certificate = await this.cacheAdapter.getCertificate(
id.toLowerCase(),
this.wallet.privateKey
);
certificates.set(id.toLowerCase(), certificate);
} catch (error) {
throw error;
}
}
return Object.assign({}, certificates.get(id.toLowerCase()));
}
public async getCertificateHistoryByCertificate(
certificateUuid: string,
parentsDepth: number = 10,
childrenDepth: number = 10
): Promise<CertificateHistoryResponseEntryInterface[]> {
return this.cacheAdapter.getCertificateHistoryByCertificate(
certificateUuid,
parentsDepth,
childrenDepth
);
}
public async getCertificateHistoryByOwner(
entityId: string
): Promise<CertificateHistoryResponseEntryInterface[]> {
return this.cacheAdapter.getCertificateHistoryByOwner(entityId);
}
/**
* Get all certificates for the authenticated account and store them on the runtime
* using global constants (@TODO change this behaviour)
*/
public async getCertificates(): Promise<any> {
try {
const ownerUuid = await this.minespider.getCurrentAccountAddress();
const entries = await this.cacheAdapter.getCertificates(ownerUuid, this.wallet.privateKey)
await this.refreshEntitiesCache();
const certificateEnvelopes = await this.minespider.getMyCertificateEnvelopes();
certificateEnvelopes.map((certificateEnvelope) => {
certificateEnvelopesMap.set(
certificateEnvelope.manifest.uuid.toLowerCase(),
certificateEnvelope
);
});
return Promise.all(
entries.map(async (certificateDTO: CertificateDTO) => {
const certificateEnvelope = certificateEnvelopesMap.get(
certificateDTO.uuid.toLowerCase()
);
certificates.set(certificateDTO.uuid.toLowerCase(), {
...certificateDTO,
metadataFiles: {
public: certificateEnvelope.publicMetadataFiles,
private: certificateEnvelope.privateMetadataFiles,
},
});
const certificate = certificates.get(certificateDTO.uuid.toLowerCase());
return Object.assign({}, certificate);
})
);
} catch (error) {
throw error;
}
}
public async getCertificatesFromCertificateEnvelopes(): Promise<any> {
try {
const entries = [];
const certificateEnvelopes = await this.minespider.getMyCertificateEnvelopes();
await Promise.all(certificateEnvelopes.map(async (certificateEnvelope) => {
certificateEnvelopesMap.set(
certificateEnvelope.manifest.uuid.toLowerCase(),
certificateEnvelope
);
const certificate = await this.cacheAdapter.getCertificate(
certificateEnvelope.manifest.uuid,
this.wallet.privateKey
)
entries.push(certificate)
}));
return entries;
} catch (error) {
throw error;
}
}
private async refreshEntitiesCache() {
(await this.minespider.getEntities()).map((entityDTO) => {
entities.set(entityDTO.id.toLowerCase(), entityDTO);
});
}
/**
* General method for creation of different entities (should not be used directly, use the aliases instead)
*
* @param name
* @param latitude
* @param longitude
* @param location
* @param meta
* @param entityType
*/
public async createEntity(
name: string,
latitude: string,
longitude: string,
location: string,
meta: object,
entityType: number,
materialMassBalanceCertifications?: MaterialMassBalanceCertification[]
) {
try {
const account = await this.minespider.registerEntity(
entityType,
name,
latitude,
longitude,
location,
materialMassBalanceCertifications
);
await this.cacheAdapter.registerClient({
uuid: account.address.toString(),
type: entityType,
mnemonic: account.mnemonic.toString(),
publicKey: account.keyPair.publicKey.toString(),
});
await this.refreshEntitiesCache();
return account;
} catch (error) {
throw error;
}
}
/**
* Alias for creation of the entity type Certifier
*
* @param name
* @param latitude
* @param longitude
* @param location
* @param meta
*/
public async createCertifier(
name: string,
latitude: string,
longitude: string,
location: string,
meta = {}
) {
return this.createEntity(
name,
latitude,
longitude,
location,
meta,
EntityType.Certifier
);
}
/**
* Alias for creation of the entity type Certifier
*
* @param name
* @param latitude
* @param longitude
* @param location
* @param meta
*/
public async createProducer(
name: string,
latitude: string,
longitude: string,
location: string,
meta = {}
) {
return this.createEntity(
name,
latitude,
longitude,
location,
meta,
EntityType.Producer
);
}
/**
* Modify Producer entity allowance to produce materialTaxonomyUuid (this is the TOTAL allowance)
*
* @param producerId
* @param materialTaxonomyUuid
* @param amount
*/
public async modifyMassBalanceCertification(
producerId: string,
materialTaxonomyUuid: string,
measurementUnitUuid: string,
amount: number
): Promise<any> {
let result = await this.minespider.modifyMassBalanceCertification(
producerId,
materialTaxonomyUuid,
measurementUnitUuid,
amount
);
return result;
}
/**
* Modify Producer entity allowance to produce materialTaxonomyUuid (this is the TOTAL allowance)
*
* @param producerId
* @param materialTaxonomyUuid
* @param amount
*/
public async modifyMassBalanceCertificationForMultipleMaterials(
producerId: string,
materialMassBalanceCertifications: MaterialMassBalanceCertification[]
): Promise<any> {
let result = await this.minespider.modifyMassBalanceCertificationForMultipleMaterials(
producerId,
materialMassBalanceCertifications
);
return result;
}
public async getEntityDetails(entityId: string) {
try {
return await this.minespider.getEntityDetails(entityId);
} catch (error) {
throw error;
}
}
public async getEntitiesByName(entityName: string): Promise<EntityDTO[]> {
try {
const entities = await this.getEntities();
return entities.filter(entity => entity.name.toLowerCase().indexOf(entityName.toLowerCase()) > -1);
} catch (error) {
throw error;
}
}
public async getEntities(): Promise<EntityDTO[]> {
if (entities.size == 0) {
await this.refreshEntitiesCache();
}
return Array.from(entities.values());
}
/**
* @TODO
* @param producerId
*/
public async getProducerMBCertification(
producerId: string,
materialTaxonomyUuid: string,
measurementUnitUuid: string
): Promise<any> {
try {
return await this.minespider.getProducerMBCertification(
producerId,
materialTaxonomyUuid,
measurementUnitUuid
);
} catch (error) {
throw error;
}
}
public async downloadCertificateEnvelope(
address: string
): Promise<CertificateEnvelope> {
return this.minespider.downloadCertificateEnvelope(address);
}
/**
* Create a Certified Data Package (Certificate) with Minespider Protocol into the Blockchain
*
* @param certificate
*/
public async createCertificate(certificate: any): Promise<string> {
try {
const response = await this.minespider.createCertificate(
certificate.certificateAmount,
certificate.certificateRawAmount,
certificate.certificateGrade,
certificate.certificateUnit,
certificate.certificateProducer,
certificate.materialTaxonomyUuid,
certificate.materialTypeUuid,
certificate.publicFiles,
certificate.privateFiles,
certificate.meta
);
await this.getCertificates();
return response;
} catch (error) {
throw error;
}
}
/**
* Sell a Certified Data Package (Certificate) with Minespider Protocol and assign it to a given address
*
* @param packetId
* @param cmw
* @param newOwner
* @param publicFilesList
* @param privateFilesList
*/
public async sellCertificate(
certificateUuid: string,
amount: number,
newOwnerAddress: string,
publicFilesList: CertificateFile[],
privateFilesList: CertificateFile[],
newCertificateType?: number,
meta?: BaseMetadataInterface
): Promise<any> {
try {
if (
certificates.size <= 0 ||
!certificates.has(certificateUuid.toLowerCase())
) {
await this.getCertificates();
} //@TODO this is a workaround remove for getting the certificate envelope directly instead
const certificateEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
const sellCertificateResponse = await this.minespider.sellCertificate(
certificateEnvelopesMap.get(certificateUuid.toLowerCase()),
amount,
newOwnerAddress,
publicFilesList,
privateFilesList,
newCertificateType,
meta
);
const certificateDTO = await this.cacheAdapter.getCertificate(
certificateEnvelope.manifest.uuid.toLowerCase(),
this.wallet.privateKey
);
certificates.set(certificateEnvelope.manifest.uuid.toLowerCase(), {
...certificateDTO,
metadataFiles: {
public: certificateEnvelope.publicMetadataFiles,
private: certificateEnvelope.privateMetadataFiles,
},
});
return sellCertificateResponse;
} catch (error) {
throw error;
}
}
/**
* Retrieve the list of all public files for a given Certificate Id and returns it's data
*
* @param certificateUuid
*/
public async getCertificatePublicFiles(
certificateUuid: string
): Promise<any> {
if (
certificates.size <= 0 ||
!certificates.has(certificateUuid.toLowerCase())
) {
await this.getCertificates();
} //@TODO this is a workaround remove for getting the certificate envelope directly instead
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
return dataPacketEnvelope.publicMetadataFiles;
}
/**
* Retrieve the list of all private files for a given CertificateUuid and returns it's data
*
* @param certificateUuid
*/
public async getCertificatePrivateFiles(
certificateUuid: string
): Promise<any> {
if (
certificates.size <= 0 ||
!certificates.has(certificateUuid.toLowerCase())
) {
await this.getCertificates();
} //@TODO this is a workaround remove for getting the certificate envelope directly instead
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
return dataPacketEnvelope.privateMetadataFiles;
}
public async downloadCertificatePublicFile(
certificateUuid: string,
fileAddress: string
) {
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
const fileMetadata = dataPacketEnvelope.publicMetadataFiles.find((file) => {
return file.target === fileAddress;
});
if (!fileMetadata) {
throw new Error(`No file metadata found for the address ${fileAddress}`);
}
return this.minespider.downloadFile(
dataPacketEnvelope.publicFilesKey,
fileMetadata
);
}
public async downloadCertificatePrivateFile(
certificateUuid: string,
fileAddress: string
) {
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
const fileMetadata = dataPacketEnvelope.privateMetadataFiles.find(
(file) => {
return file.target === fileAddress;
}
);
if (!fileMetadata) {
throw new Error(`No file metadata found for the address ${fileAddress}`);
}
return this.minespider.downloadFile(
dataPacketEnvelope.privateFilesKey,
fileMetadata
);
}
public async getCurrentAccountAddress() {
return await this.minespider.getCurrentAccountAddress();
}
public async getCertificateManifest(certificateUuid: string) {
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
return dataPacketEnvelope.manifest;
}
public async readCertificateFileList(
fileList: FileList
): Promise<CertificateFile[]> {
var certificateFiles: CertificateFile[] = [];
for (let i = 0; i < fileList.length; i++) {
var fileContent = await new Promise<Buffer>((resolve, reject) => {
var reader = new FileReader();
reader.onload = function (event: any) {
resolve(Buffer.from(event.target.result));
};
reader.readAsArrayBuffer(fileList[i]);
});
certificateFiles.push(
new CertificateFile(fileContent, fileList[i].name, {
type: fileList[i].type,
})
);
}
return certificateFiles;
}
public async getMaterialAllowances(
balanceHolderUuid: string,
materialTypeUuid: string,
measurementUnitUuid: string
): Promise<AllowanceEntry[]> {
return this.minespider.getMaterialAllowances(
balanceHolderUuid,
materialTypeUuid,
measurementUnitUuid
);
}
public async getMaterialTaxonomies() {
return this.minespider.getMaterialTaxonomies();
}
public async getMaterialTypes() {
return this.minespider.getMaterialTypes();
}
public async getMeasurementUnits() {
return this.minespider.getMeasurementUnits();
}
public async getMeasurementUnitTypes() {
return this.minespider.getMeasurementUnitTypes();
}
/**
* Get private metadata information
* @param certificateUuid
* @param path, ex: 'header.sdkVersion'
*/
public async getPrivateMetadata(
certificateUuid: string,
path?: string
): Promise<unknown> {
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
const fileMetadata = dataPacketEnvelope.privateMetadataFiles
.filter((file) => {
return file.filename === "minespider.json";
})
.pop();
if (!fileMetadata) {
throw new Error(`${ERROR_NO_PRIVATE_METADATA} ${certificateUuid}`);
}
const file = await this.minespider.downloadFile(
dataPacketEnvelope.privateFilesKey,
fileMetadata
);
try {
const metadata = JSON.parse(file.buffer.toString());
return this.getObjectPath(metadata, path);
} catch (e) {
throw e;
}
}
/**
* Get public metadata information
* @param certificateUuid
* @param path, ex: 'header.sdkVersion'
*/
public async getPublicMetadata(
certificateUuid: string,
path?: string
): Promise<unknown> {
const dataPacketEnvelope = certificateEnvelopesMap.get(
certificateUuid.toLowerCase()
);
const fileMetadata = dataPacketEnvelope.publicMetadataFiles
.filter((file) => {
return file.filename === "minespider.json";
})
.pop();
if (!fileMetadata) {
throw new Error(`${ERROR_NO_PUBLIC_METADATA} ${certificateUuid}`);
}
const file = await this.minespider.downloadFile(
dataPacketEnvelope.publicFilesKey,
fileMetadata
);
try {
const metadata: JSONInterface = JSON.parse(file.buffer.toString());
return this.getObjectPath(metadata, path);
} catch (e) {
throw e;
}
}
private getObjectPath(obj: JSONInterface, path: string): unknown {
if (!path) {
return obj;
}
return path.split(".").reduce((acc, key) => {
if (!acc[key]) {
throw new Error(ERROR_PATH_NOT_EXIST);
}
acc = acc[key];
return acc;
}, obj);
}
public async getCertificateOwner(certificateId: string): Promise<EntityDTO> {
if (
certificates.size <= 0 ||
!certificates.has(certificateId.toLowerCase())
) {
await this.getCertificates();
}
const certificate = await this.cacheAdapter.getCertificate(
certificateId.toLowerCase(),
this.wallet.privateKey
)
if (!certificate) {
return;
}
if (entities.has(certificate.owner.uuid.toLowerCase())) {
const entity = entities.get(certificate.owner.uuid.toLowerCase())
return entity;
}
const entity = {
...(await this.minespider.getEntityDetails(certificate.owner.uuid.toLowerCase())),
entityType: certificate.owner.type
}
if (!entity.id) {
return;
}
entities.set(entity.id.toLowerCase(), entity);
return entity;
}
public async getCertificateParentOwner(
certificateId: string
): Promise<EntityDTO> {
if (
certificates.size <= 0 ||
!certificates.has(certificateId.toLowerCase())
) {
await this.getCertificates();
}
const certificate = certificates.get(certificateId.toLowerCase())
const parentCertificate = await this.cacheAdapter.getCertificate(
certificate.details.parent.toLowerCase(),
this.wallet.privateKey
)
if (!parentCertificate) {
return;
}
if (entities.has(parentCertificate.owner.uuid.toLowerCase())) {
return entities.get(parentCertificate.owner.uuid.toLowerCase());
}
const entity = {
...(await this.minespider.getEntityDetails(parentCertificate.owner.uuid.toLowerCase())),
entityType: parentCertificate.owner.type
}
entities.set(entity.id.toLowerCase(), entity);
return entity;
}
public async getBalanceHolderUuid() {
return this.minespider.getBalanceHolderUuid();
}
public async acceptCertificate(certificateUuid: string): Promise<void> {
return this.minespider.acceptCertificate(certificateUuid);
}
}