mirror of
https://github.com/oceanprotocol/ocean.js.git
synced 2024-11-26 20:39:05 +01:00
added ddo object param to function that expect did and asset resolve is called
This commit is contained in:
parent
2fb83f97a9
commit
f0404f3204
@ -47,7 +47,10 @@ export class DataTokens {
|
|||||||
* Generate new datatoken name & symbol from a word list
|
* Generate new datatoken name & symbol from a word list
|
||||||
* @return {<{ name: String; symbol: String }>} datatoken name & symbol. Produces e.g. "Endemic Jellyfish Token" & "ENDJEL-45"
|
* @return {<{ name: String; symbol: String }>} datatoken name & symbol. Produces e.g. "Endemic Jellyfish Token" & "ENDJEL-45"
|
||||||
*/
|
*/
|
||||||
public generateDtName(wordList?: { nouns: string[]; adjectives: string[] }): {
|
public generateDtName(wordList?: {
|
||||||
|
nouns: string[]
|
||||||
|
adjectives: string[]
|
||||||
|
}): {
|
||||||
name: string
|
name: string
|
||||||
symbol: string
|
symbol: string
|
||||||
} {
|
} {
|
||||||
|
@ -452,11 +452,18 @@ export class Assets extends Instantiable {
|
|||||||
serviceType: string,
|
serviceType: string,
|
||||||
consumerAddress: string,
|
consumerAddress: string,
|
||||||
serviceIndex = -1,
|
serviceIndex = -1,
|
||||||
serviceEndpoint: string
|
serviceEndpoint: string,
|
||||||
|
ddo?: DDO
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
await provider.setBaseUrl(serviceEndpoint)
|
await provider.setBaseUrl(serviceEndpoint)
|
||||||
const res = await provider.initialize(did, serviceIndex, serviceType, consumerAddress)
|
const res = await provider.initialize(
|
||||||
|
did,
|
||||||
|
serviceIndex,
|
||||||
|
serviceType,
|
||||||
|
consumerAddress,
|
||||||
|
ddo
|
||||||
|
)
|
||||||
if (res === null) return null
|
if (res === null) return null
|
||||||
const providerData = JSON.parse(res)
|
const providerData = JSON.parse(res)
|
||||||
return providerData
|
return providerData
|
||||||
@ -501,7 +508,8 @@ export class Assets extends Instantiable {
|
|||||||
serviceType,
|
serviceType,
|
||||||
payerAddress,
|
payerAddress,
|
||||||
serviceIndex,
|
serviceIndex,
|
||||||
service.serviceEndpoint
|
service.serviceEndpoint,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
if (!providerData)
|
if (!providerData)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -105,6 +105,7 @@ export class Compute extends Instantiable {
|
|||||||
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
||||||
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
||||||
* @param {Output} output Define algorithm output publishing. Publishing the result of a compute job is turned off by default.
|
* @param {Output} output Define algorithm output publishing. Publishing the result of a compute job is turned off by default.
|
||||||
|
* @param {DDO} ddo If undefined then the ddo will be fetched by did
|
||||||
* @return {Promise<ComputeJob>} Returns compute job ID under status.jobId
|
* @return {Promise<ComputeJob>} Returns compute job ID under status.jobId
|
||||||
*/
|
*/
|
||||||
public async start(
|
public async start(
|
||||||
@ -116,10 +117,14 @@ export class Compute extends Instantiable {
|
|||||||
output?: ComputeOutput,
|
output?: ComputeOutput,
|
||||||
serviceIndex?: string,
|
serviceIndex?: string,
|
||||||
serviceType?: string,
|
serviceType?: string,
|
||||||
additionalInputs?: ComputeInput[]
|
additionalInputs?: ComputeInput[],
|
||||||
|
ddo?: DDO
|
||||||
): Promise<ComputeJob> {
|
): Promise<ComputeJob> {
|
||||||
output = this.checkOutput(consumerAccount, output)
|
output = this.checkOutput(consumerAccount, output)
|
||||||
const ddo = await this.ocean.assets.resolve(did)
|
if (!ddo) {
|
||||||
|
ddo = await this.ocean.assets.resolve(did)
|
||||||
|
if (!ddo) throw new Error(`Couldn't resolve the did ${did}`)
|
||||||
|
}
|
||||||
const service = ddo.findServiceByType('compute')
|
const service = ddo.findServiceByType('compute')
|
||||||
const { serviceEndpoint } = service
|
const { serviceEndpoint } = service
|
||||||
if (did && txId) {
|
if (did && txId) {
|
||||||
@ -147,14 +152,19 @@ export class Compute extends Instantiable {
|
|||||||
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
||||||
* @param {string} did Decentralized identifier.
|
* @param {string} did Decentralized identifier.
|
||||||
* @param {string} jobId The ID of the compute job to be stopped
|
* @param {string} jobId The ID of the compute job to be stopped
|
||||||
|
* @param {DDO} ddo If undefined then the ddo will be fetched by did
|
||||||
* @return {Promise<ComputeJob>} Returns the new status of a job
|
* @return {Promise<ComputeJob>} Returns the new status of a job
|
||||||
*/
|
*/
|
||||||
public async stop(
|
public async stop(
|
||||||
consumerAccount: Account,
|
consumerAccount: Account,
|
||||||
did: string,
|
did: string,
|
||||||
jobId: string
|
jobId: string,
|
||||||
|
ddo?: DDO
|
||||||
): Promise<ComputeJob> {
|
): Promise<ComputeJob> {
|
||||||
const ddo = await this.ocean.assets.resolve(did)
|
if (!ddo) {
|
||||||
|
ddo = await this.ocean.assets.resolve(did)
|
||||||
|
if (!ddo) throw new Error(`Couldn't resolve the did ${did}`)
|
||||||
|
}
|
||||||
const service = ddo.findServiceByType('compute')
|
const service = ddo.findServiceByType('compute')
|
||||||
const { serviceEndpoint } = service
|
const { serviceEndpoint } = service
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
@ -169,14 +179,19 @@ export class Compute extends Instantiable {
|
|||||||
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
||||||
* @param {string} did Decentralized identifier.
|
* @param {string} did Decentralized identifier.
|
||||||
* @param {string} jobId The ID of the compute job to be stopped
|
* @param {string} jobId The ID of the compute job to be stopped
|
||||||
|
* @param {DDO} ddo If undefined then the ddo will be fetched by did
|
||||||
* @return {Promise<ComputeJob>} Returns the new status of a job
|
* @return {Promise<ComputeJob>} Returns the new status of a job
|
||||||
*/
|
*/
|
||||||
public async delete(
|
public async delete(
|
||||||
consumerAccount: Account,
|
consumerAccount: Account,
|
||||||
did: string,
|
did: string,
|
||||||
jobId: string
|
jobId: string,
|
||||||
|
ddo?: DDO
|
||||||
): Promise<ComputeJob> {
|
): Promise<ComputeJob> {
|
||||||
const ddo = await this.ocean.assets.resolve(did)
|
if (!ddo) {
|
||||||
|
ddo = await this.ocean.assets.resolve(did)
|
||||||
|
if (!ddo) throw new Error(`Couldn't resolve the did ${did}`)
|
||||||
|
}
|
||||||
const service = ddo.findServiceByType('compute')
|
const service = ddo.findServiceByType('compute')
|
||||||
const { serviceEndpoint } = service
|
const { serviceEndpoint } = service
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
@ -243,14 +258,19 @@ export class Compute extends Instantiable {
|
|||||||
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
* @param {Account} consumerAccount The account of the consumer ordering the service.
|
||||||
* @param {string} did Decentralized identifier.
|
* @param {string} did Decentralized identifier.
|
||||||
* @param {string} jobId The ID of the compute job to be stopped.
|
* @param {string} jobId The ID of the compute job to be stopped.
|
||||||
|
* @param {DDO} ddo If undefined then the ddo will be fetched by did
|
||||||
* @return {Promise<ComputeJob>} Returns the DDO of the result asset.
|
* @return {Promise<ComputeJob>} Returns the DDO of the result asset.
|
||||||
*/
|
*/
|
||||||
public async result(
|
public async result(
|
||||||
consumerAccount: Account,
|
consumerAccount: Account,
|
||||||
did: string,
|
did: string,
|
||||||
jobId: string
|
jobId: string,
|
||||||
|
ddo?: DDO
|
||||||
): Promise<ComputeJob> {
|
): Promise<ComputeJob> {
|
||||||
const ddo = await this.ocean.assets.resolve(did)
|
if (!ddo) {
|
||||||
|
ddo = await this.ocean.assets.resolve(did)
|
||||||
|
if (!ddo) throw new Error(`Couldn't resolve the did ${did}`)
|
||||||
|
}
|
||||||
const service = ddo.findServiceByType('compute')
|
const service = ddo.findServiceByType('compute')
|
||||||
const { serviceEndpoint } = service
|
const { serviceEndpoint } = service
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
@ -401,6 +421,8 @@ export class Compute extends Instantiable {
|
|||||||
* @param {string} serviceIndex The Service index
|
* @param {string} serviceIndex The Service index
|
||||||
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
||||||
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
||||||
|
* @param {DDO} datasetDdo Dataset DDO object. If undefined then the ddo will be fetched by did
|
||||||
|
* @param {DDO} algorithmDDO Algorithm DDO object. If undefined then the ddo will be fetched by did
|
||||||
* @return {Promise<boolean>} True is you can order this
|
* @return {Promise<boolean>} True is you can order this
|
||||||
*
|
*
|
||||||
* Note: algorithmDid and algorithmMeta are optional, but if they are not passed,
|
* Note: algorithmDid and algorithmMeta are optional, but if they are not passed,
|
||||||
@ -410,10 +432,15 @@ export class Compute extends Instantiable {
|
|||||||
public async isOrderable(
|
public async isOrderable(
|
||||||
datasetDid: string,
|
datasetDid: string,
|
||||||
serviceIndex: number,
|
serviceIndex: number,
|
||||||
algorithm: ComputeAlgorithm
|
algorithm: ComputeAlgorithm,
|
||||||
|
datasetDdo?: DDO,
|
||||||
|
algorithmDDO?: DDO
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const ddo: DDO = await this.ocean.assets.resolve(datasetDid)
|
if (!datasetDdo) {
|
||||||
const service: Service = ddo.findServiceById(serviceIndex)
|
datasetDdo = await this.ocean.assets.resolve(datasetDid)
|
||||||
|
if (!datasetDdo) throw new Error(`Couldn't resolve the did ${datasetDid}`)
|
||||||
|
}
|
||||||
|
const service: Service = datasetDdo.findServiceById(serviceIndex)
|
||||||
if (!service) return false
|
if (!service) return false
|
||||||
if (service.type === 'compute') {
|
if (service.type === 'compute') {
|
||||||
if (algorithm.meta) {
|
if (algorithm.meta) {
|
||||||
@ -426,8 +453,14 @@ export class Compute extends Instantiable {
|
|||||||
if (algorithm.did) {
|
if (algorithm.did) {
|
||||||
// check if both have compute services and then if they are served by the same provider
|
// check if both have compute services and then if they are served by the same provider
|
||||||
if (algorithm.serviceIndex) {
|
if (algorithm.serviceIndex) {
|
||||||
const algoDDO: DDO = await this.ocean.assets.resolve(algorithm.did)
|
if (!algorithmDDO) {
|
||||||
const algoService: Service = algoDDO.findServiceById(algorithm.serviceIndex)
|
algorithmDDO = await this.ocean.assets.resolve(algorithm.did)
|
||||||
|
if (!algorithmDDO)
|
||||||
|
throw new Error(`Couldn't resolve the did ${algorithm.did}`)
|
||||||
|
}
|
||||||
|
const algoService: Service = algorithmDDO.findServiceById(
|
||||||
|
algorithm.serviceIndex
|
||||||
|
)
|
||||||
if (algoService && algoService.type === 'compute') {
|
if (algoService && algoService.type === 'compute') {
|
||||||
// since both dataset & algo services are compute, we need to check if they are served by the same provider
|
// since both dataset & algo services are compute, we need to check if they are served by the same provider
|
||||||
const algoProvider = await Provider.getInstance(this.instanceConfig)
|
const algoProvider = await Provider.getInstance(this.instanceConfig)
|
||||||
@ -511,18 +544,28 @@ export class Compute extends Instantiable {
|
|||||||
algorithm: ComputeAlgorithm,
|
algorithm: ComputeAlgorithm,
|
||||||
mpAddress?: string,
|
mpAddress?: string,
|
||||||
computeAddress?: string,
|
computeAddress?: string,
|
||||||
|
datasetDdo?: DDO,
|
||||||
searchPreviousOrders = true
|
searchPreviousOrders = true
|
||||||
): SubscribablePromise<OrderProgressStep, string> {
|
): SubscribablePromise<OrderProgressStep, string> {
|
||||||
return new SubscribablePromise(async (observer) => {
|
return new SubscribablePromise(async (observer) => {
|
||||||
// first check if we can order this
|
// first check if we can order this
|
||||||
const allowed = await this.isOrderable(datasetDid, serviceIndex, algorithm)
|
const allowed = await this.isOrderable(
|
||||||
|
datasetDid,
|
||||||
|
serviceIndex,
|
||||||
|
algorithm,
|
||||||
|
datasetDdo
|
||||||
|
)
|
||||||
if (!allowed)
|
if (!allowed)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Dataset order failed, dataset is not orderable with the specified algorithm`
|
`Dataset order failed, dataset is not orderable with the specified algorithm`
|
||||||
)
|
)
|
||||||
const ddo: DDO = await this.ocean.assets.resolve(datasetDid)
|
|
||||||
|
if (!datasetDdo) {
|
||||||
|
datasetDdo = await this.ocean.assets.resolve(datasetDid)
|
||||||
|
if (!datasetDdo) throw new Error(`Couldn't resolve the did ${datasetDid}`)
|
||||||
|
}
|
||||||
// const service: Service = ddo.findServiceByType('compute')
|
// const service: Service = ddo.findServiceByType('compute')
|
||||||
const service: Service = ddo.findServiceById(serviceIndex)
|
const service: Service = datasetDdo.findServiceById(serviceIndex)
|
||||||
if (!service)
|
if (!service)
|
||||||
throw new Error(`Dataset order failed, Could not find service for the DDO`)
|
throw new Error(`Dataset order failed, Could not find service for the DDO`)
|
||||||
try {
|
try {
|
||||||
@ -631,8 +674,9 @@ export class Compute extends Instantiable {
|
|||||||
}
|
}
|
||||||
if (typeof ddo.service[serviceIndex] === 'undefined') return null
|
if (typeof ddo.service[serviceIndex] === 'undefined') return null
|
||||||
if (ddo.service[serviceIndex].type !== 'compute') return null
|
if (ddo.service[serviceIndex].type !== 'compute') return null
|
||||||
ddo.service[serviceIndex].attributes.main.privacy.allowAllPublishedAlgorithms =
|
ddo.service[
|
||||||
newState
|
serviceIndex
|
||||||
|
].attributes.main.privacy.allowAllPublishedAlgorithms = newState
|
||||||
return ddo
|
return ddo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,12 +791,13 @@ export class Compute extends Instantiable {
|
|||||||
if (ddo.service[serviceIndex].type !== 'compute') return ddo
|
if (ddo.service[serviceIndex].type !== 'compute') return ddo
|
||||||
if (!ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms)
|
if (!ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms)
|
||||||
return ddo
|
return ddo
|
||||||
ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms =
|
ddo.service[
|
||||||
ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms.filter(
|
serviceIndex
|
||||||
function (el) {
|
].attributes.main.privacy.publisherTrustedAlgorithms = ddo.service[
|
||||||
|
serviceIndex
|
||||||
|
].attributes.main.privacy.publisherTrustedAlgorithms.filter(function (el) {
|
||||||
return el.did !== algoDid
|
return el.did !== algoDid
|
||||||
}
|
})
|
||||||
)
|
|
||||||
return ddo
|
return ddo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,15 +189,12 @@ export class Provider extends Instantiable {
|
|||||||
did: string,
|
did: string,
|
||||||
serviceIndex: number,
|
serviceIndex: number,
|
||||||
serviceType: string,
|
serviceType: string,
|
||||||
consumerAddress: string
|
consumerAddress: string,
|
||||||
|
ddo?: DDO
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let DDO: DDO
|
if (!ddo) {
|
||||||
|
ddo = await this.ocean.assets.resolve(did)
|
||||||
try {
|
if (!ddo) throw new Error(`Couldn't resolve the did ${did}`)
|
||||||
DDO = await this.ocean.assets.resolve(did)
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(e)
|
|
||||||
throw new Error('Failed to resolve DID')
|
|
||||||
}
|
}
|
||||||
let initializeUrl = this.getInitializeEndpoint()
|
let initializeUrl = this.getInitializeEndpoint()
|
||||||
? this.getInitializeEndpoint().urlPath
|
? this.getInitializeEndpoint().urlPath
|
||||||
@ -206,7 +203,7 @@ export class Provider extends Instantiable {
|
|||||||
initializeUrl += `?documentId=${did}`
|
initializeUrl += `?documentId=${did}`
|
||||||
initializeUrl += `&serviceId=${serviceIndex}`
|
initializeUrl += `&serviceId=${serviceIndex}`
|
||||||
initializeUrl += `&serviceType=${serviceType}`
|
initializeUrl += `&serviceType=${serviceType}`
|
||||||
initializeUrl += `&dataToken=${DDO.dataToken}`
|
initializeUrl += `&dataToken=${ddo.dataToken}`
|
||||||
initializeUrl += `&consumerAddress=${consumerAddress}`
|
initializeUrl += `&consumerAddress=${consumerAddress}`
|
||||||
try {
|
try {
|
||||||
const response = await this.ocean.utils.fetch.get(initializeUrl)
|
const response = await this.ocean.utils.fetch.get(initializeUrl)
|
||||||
|
@ -150,8 +150,14 @@ export class ConfigHelper {
|
|||||||
// use the defaults first
|
// use the defaults first
|
||||||
let configAddresses: Partial<ConfigHelperConfig>
|
let configAddresses: Partial<ConfigHelperConfig>
|
||||||
if (DefaultContractsAddresses[network]) {
|
if (DefaultContractsAddresses[network]) {
|
||||||
const { DTFactory, BFactory, FixedRateExchange, Dispenser, Metadata, Ocean } =
|
const {
|
||||||
DefaultContractsAddresses[network]
|
DTFactory,
|
||||||
|
BFactory,
|
||||||
|
FixedRateExchange,
|
||||||
|
Dispenser,
|
||||||
|
Metadata,
|
||||||
|
Ocean
|
||||||
|
} = DefaultContractsAddresses[network]
|
||||||
configAddresses = {
|
configAddresses = {
|
||||||
factoryAddress: DTFactory,
|
factoryAddress: DTFactory,
|
||||||
poolFactoryAddress: BFactory,
|
poolFactoryAddress: BFactory,
|
||||||
@ -172,8 +178,14 @@ export class ConfigHelper {
|
|||||||
'utf8'
|
'utf8'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const { DTFactory, BFactory, FixedRateExchange, Dispenser, Metadata, Ocean } =
|
const {
|
||||||
data[network]
|
DTFactory,
|
||||||
|
BFactory,
|
||||||
|
FixedRateExchange,
|
||||||
|
Dispenser,
|
||||||
|
Metadata,
|
||||||
|
Ocean
|
||||||
|
} = data[network]
|
||||||
configAddresses = {
|
configAddresses = {
|
||||||
factoryAddress: DTFactory,
|
factoryAddress: DTFactory,
|
||||||
poolFactoryAddress: BFactory,
|
poolFactoryAddress: BFactory,
|
||||||
|
@ -17,7 +17,9 @@ export class SubscribablePromise<T extends any, P extends any> {
|
|||||||
setTimeout(() => this.init(executor), 1)
|
setTimeout(() => this.init(executor), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribe(onNext: (next: T) => void): {
|
public subscribe(
|
||||||
|
onNext: (next: T) => void
|
||||||
|
): {
|
||||||
unsubscribe: () => boolean
|
unsubscribe: () => boolean
|
||||||
} {
|
} {
|
||||||
return this.observer.subscribe(onNext)
|
return this.observer.subscribe(onNext)
|
||||||
|
@ -104,7 +104,8 @@ describe('Compute flow', () => {
|
|||||||
language: 'js',
|
language: 'js',
|
||||||
format: 'docker-image',
|
format: 'docker-image',
|
||||||
version: '0.1',
|
version: '0.1',
|
||||||
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
container: {
|
container: {
|
||||||
entrypoint: 'node $ALGO',
|
entrypoint: 'node $ALGO',
|
||||||
image: 'node',
|
image: 'node',
|
||||||
@ -249,7 +250,8 @@ describe('Compute flow', () => {
|
|||||||
license: 'CC-BY',
|
license: 'CC-BY',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
url: 'https://raw.githubusercontent.com/tbertinmahieux/MSongsDB/master/Tasks_Demos/CoverSongs/shs_dataset_test.txt',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/tbertinmahieux/MSongsDB/master/Tasks_Demos/CoverSongs/shs_dataset_test.txt',
|
||||||
checksum: 'efb2c764274b745f5fc37f97c6b0e764',
|
checksum: 'efb2c764274b745f5fc37f97c6b0e764',
|
||||||
contentLength: '4535431',
|
contentLength: '4535431',
|
||||||
contentType: 'text/csv',
|
contentType: 'text/csv',
|
||||||
@ -485,7 +487,8 @@ describe('Compute flow', () => {
|
|||||||
license: 'CC-BY',
|
license: 'CC-BY',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
contentType: 'text/js',
|
contentType: 'text/js',
|
||||||
encoding: 'UTF-8'
|
encoding: 'UTF-8'
|
||||||
}
|
}
|
||||||
@ -537,7 +540,8 @@ describe('Compute flow', () => {
|
|||||||
license: 'CC-BY',
|
license: 'CC-BY',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
contentType: 'text/js',
|
contentType: 'text/js',
|
||||||
encoding: 'UTF-8'
|
encoding: 'UTF-8'
|
||||||
}
|
}
|
||||||
@ -598,7 +602,8 @@ describe('Compute flow', () => {
|
|||||||
license: 'CC-BY',
|
license: 'CC-BY',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
contentType: 'text/js',
|
contentType: 'text/js',
|
||||||
encoding: 'UTF-8'
|
encoding: 'UTF-8'
|
||||||
}
|
}
|
||||||
@ -662,7 +667,8 @@ describe('Compute flow', () => {
|
|||||||
license: 'CC-BY',
|
license: 'CC-BY',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
url:
|
||||||
|
'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
contentType: 'text/js',
|
contentType: 'text/js',
|
||||||
encoding: 'UTF-8'
|
encoding: 'UTF-8'
|
||||||
}
|
}
|
||||||
@ -840,7 +846,8 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
computeOrderId = await ocean.compute.orderAsset(
|
computeOrderId = await ocean.compute.orderAsset(
|
||||||
@ -849,7 +856,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(computeOrderId != null, 'computeOrderId === null')
|
assert(computeOrderId != null, 'computeOrderId === null')
|
||||||
const response = await ocean.compute.start(
|
const response = await ocean.compute.start(
|
||||||
@ -860,7 +868,9 @@ describe('Compute flow', () => {
|
|||||||
algoDefinition,
|
algoDefinition,
|
||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type
|
computeService.type,
|
||||||
|
undefined,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response, 'Compute error')
|
assert(response, 'Compute error')
|
||||||
jobId = response.jobId
|
jobId = response.jobId
|
||||||
@ -918,7 +928,7 @@ describe('Compute flow', () => {
|
|||||||
})
|
})
|
||||||
it('Bob should stop compute job', async () => {
|
it('Bob should stop compute job', async () => {
|
||||||
assert(jobId != null, 'Jobid is null')
|
assert(jobId != null, 'Jobid is null')
|
||||||
await ocean.compute.stop(bob, ddo.id, jobId)
|
await ocean.compute.stop(bob, ddo.id, jobId, ddo)
|
||||||
const response = await ocean.compute.status(bob, ddo.id, undefined, undefined, jobId)
|
const response = await ocean.compute.status(bob, ddo.id, undefined, undefined, jobId)
|
||||||
// TODO: typings say that `stopreq` does not exist
|
// TODO: typings say that `stopreq` does not exist
|
||||||
assert((response[0] as any).stopreq === 1, 'Response.stopreq is invalid')
|
assert((response[0] as any).stopreq === 1, 'Response.stopreq is invalid')
|
||||||
@ -941,7 +951,8 @@ describe('Compute flow', () => {
|
|||||||
service1.index,
|
service1.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
datasetNoRawAlgo
|
||||||
)
|
)
|
||||||
assert(order === null, 'Order should be null')
|
assert(order === null, 'Order should be null')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -964,7 +975,8 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
datasetWithTrustedAlgo.id,
|
datasetWithTrustedAlgo.id,
|
||||||
service1.index,
|
service1.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
datasetWithTrustedAlgo
|
||||||
)
|
)
|
||||||
assert(allowed === false)
|
assert(allowed === false)
|
||||||
|
|
||||||
@ -976,7 +988,8 @@ describe('Compute flow', () => {
|
|||||||
service1.index,
|
service1.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
datasetWithTrustedAlgo
|
||||||
)
|
)
|
||||||
assert(order === null, 'Order should be null')
|
assert(order === null, 'Order should be null')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -995,7 +1008,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAsset
|
||||||
)
|
)
|
||||||
assert(allowed === false)
|
assert(allowed === false)
|
||||||
|
|
||||||
@ -1006,7 +1021,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order === null, 'Order should be null')
|
assert(order === null, 'Order should be null')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -1081,8 +1097,9 @@ describe('Compute flow', () => {
|
|||||||
algorithmAssetRemoteProviderWithCompute != null,
|
algorithmAssetRemoteProviderWithCompute != null,
|
||||||
'algorithmAsset should not be null'
|
'algorithmAsset should not be null'
|
||||||
)
|
)
|
||||||
const serviceAlgo =
|
const serviceAlgo = algorithmAssetRemoteProviderWithCompute.findServiceByType(
|
||||||
algorithmAssetRemoteProviderWithCompute.findServiceByType('compute')
|
'compute'
|
||||||
|
)
|
||||||
assert(serviceAlgo != null, 'serviceAlgo should not be null')
|
assert(serviceAlgo != null, 'serviceAlgo should not be null')
|
||||||
// get the compute address first
|
// get the compute address first
|
||||||
computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index)
|
computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index)
|
||||||
@ -1094,7 +1111,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAssetRemoteProviderWithCompute
|
||||||
)
|
)
|
||||||
assert(allowed === false)
|
assert(allowed === false)
|
||||||
try {
|
try {
|
||||||
@ -1104,7 +1123,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order === null, 'Order should be null')
|
assert(order === null, 'Order should be null')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -1127,7 +1147,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAssetwithCompute
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const order = await ocean.compute.orderAsset(
|
const order = await ocean.compute.orderAsset(
|
||||||
@ -1136,7 +1158,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order != null, 'Order should not be null')
|
assert(order != null, 'Order should not be null')
|
||||||
// order the algorithm
|
// order the algorithm
|
||||||
@ -1159,7 +1182,9 @@ describe('Compute flow', () => {
|
|||||||
algoDefinition,
|
algoDefinition,
|
||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type
|
computeService.type,
|
||||||
|
undefined,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response, 'Compute error')
|
assert(response, 'Compute error')
|
||||||
jobId = response.jobId
|
jobId = response.jobId
|
||||||
@ -1183,7 +1208,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAsset
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const order = await ocean.compute.orderAsset(
|
const order = await ocean.compute.orderAsset(
|
||||||
@ -1192,7 +1219,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order != null, 'Order should not be null')
|
assert(order != null, 'Order should not be null')
|
||||||
// order the algorithm
|
// order the algorithm
|
||||||
@ -1215,7 +1243,9 @@ describe('Compute flow', () => {
|
|||||||
algoDefinition,
|
algoDefinition,
|
||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type
|
computeService.type,
|
||||||
|
undefined,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response, 'Compute error')
|
assert(response, 'Compute error')
|
||||||
jobId = response.jobId
|
jobId = response.jobId
|
||||||
@ -1242,7 +1272,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAssetRemoteProvider
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const order = await ocean.compute.orderAsset(
|
const order = await ocean.compute.orderAsset(
|
||||||
@ -1251,7 +1283,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order != null, 'Order should not be null')
|
assert(order != null, 'Order should not be null')
|
||||||
const orderalgo = await ocean.compute.orderAlgorithm(
|
const orderalgo = await ocean.compute.orderAlgorithm(
|
||||||
@ -1273,7 +1306,9 @@ describe('Compute flow', () => {
|
|||||||
algoDefinition,
|
algoDefinition,
|
||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type
|
computeService.type,
|
||||||
|
undefined,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response, 'Compute error')
|
assert(response, 'Compute error')
|
||||||
assert(response.status >= 1, 'Invalid response status')
|
assert(response.status >= 1, 'Invalid response status')
|
||||||
@ -1296,7 +1331,9 @@ describe('Compute flow', () => {
|
|||||||
allowed = await ocean.compute.isOrderable(
|
allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAsset
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const order = await ocean.compute.orderAsset(
|
const order = await ocean.compute.orderAsset(
|
||||||
@ -1305,7 +1342,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(order != null, 'Order should not be null')
|
assert(order != null, 'Order should not be null')
|
||||||
// order the algo
|
// order the algo
|
||||||
@ -1324,7 +1362,8 @@ describe('Compute flow', () => {
|
|||||||
allowed = await ocean.compute.isOrderable(
|
allowed = await ocean.compute.isOrderable(
|
||||||
ddoAdditional1.id,
|
ddoAdditional1.id,
|
||||||
inputOrder1Service.index,
|
inputOrder1Service.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddoAdditional1
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const inputOrder1 = await ocean.compute.orderAsset(
|
const inputOrder1 = await ocean.compute.orderAsset(
|
||||||
@ -1333,7 +1372,8 @@ describe('Compute flow', () => {
|
|||||||
inputOrder1Service.index,
|
inputOrder1Service.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddoAdditional1
|
||||||
)
|
)
|
||||||
assert(inputOrder1 != null, 'inputOrder1 should not be null')
|
assert(inputOrder1 != null, 'inputOrder1 should not be null')
|
||||||
assert(ddoAdditional1 != null, 'ddoAdditional1 should not be null')
|
assert(ddoAdditional1 != null, 'ddoAdditional1 should not be null')
|
||||||
@ -1342,7 +1382,8 @@ describe('Compute flow', () => {
|
|||||||
allowed = await ocean.compute.isOrderable(
|
allowed = await ocean.compute.isOrderable(
|
||||||
ddoAdditional2.id,
|
ddoAdditional2.id,
|
||||||
inputOrder2Service.index,
|
inputOrder2Service.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddoAdditional2
|
||||||
)
|
)
|
||||||
assert(allowed === true)
|
assert(allowed === true)
|
||||||
const inputOrder2 = await ocean.compute.orderAsset(
|
const inputOrder2 = await ocean.compute.orderAsset(
|
||||||
@ -1351,7 +1392,8 @@ describe('Compute flow', () => {
|
|||||||
inputOrder2Service.index,
|
inputOrder2Service.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
ddoAdditional2
|
||||||
)
|
)
|
||||||
assert(inputOrder2 != null, 'inputOrder2 should not be null')
|
assert(inputOrder2 != null, 'inputOrder2 should not be null')
|
||||||
const additionalInputs: ComputeInput[] = [
|
const additionalInputs: ComputeInput[] = [
|
||||||
@ -1377,7 +1419,8 @@ describe('Compute flow', () => {
|
|||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type,
|
computeService.type,
|
||||||
additionalInputs
|
additionalInputs,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response.status >= 1, 'Invalid response.status')
|
assert(response.status >= 1, 'Invalid response.status')
|
||||||
assert(response.jobId, 'Invalid jobId')
|
assert(response.jobId, 'Invalid jobId')
|
||||||
@ -1464,7 +1507,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAsset
|
||||||
)
|
)
|
||||||
assert(allowed === false, 'This should fail, the algo container section was changed!')
|
assert(allowed === false, 'This should fail, the algo container section was changed!')
|
||||||
})
|
})
|
||||||
@ -1500,7 +1545,9 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
ddo.id,
|
ddo.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
ddo,
|
||||||
|
algorithmAssetRemoteProvider
|
||||||
)
|
)
|
||||||
assert(allowed === false, 'This should fail, the algo files section was changed!')
|
assert(allowed === false, 'This should fail, the algo files section was changed!')
|
||||||
})
|
})
|
||||||
@ -1556,7 +1603,8 @@ describe('Compute flow', () => {
|
|||||||
const allowed = await ocean.compute.isOrderable(
|
const allowed = await ocean.compute.isOrderable(
|
||||||
datasetWithBogusProvider.id,
|
datasetWithBogusProvider.id,
|
||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition
|
algoDefinition,
|
||||||
|
datasetWithBogusProvider
|
||||||
)
|
)
|
||||||
assert(allowed === false)
|
assert(allowed === false)
|
||||||
// we know that it is not Orderable, but we are trying to force it
|
// we know that it is not Orderable, but we are trying to force it
|
||||||
@ -1568,7 +1616,8 @@ describe('Compute flow', () => {
|
|||||||
computeService.index,
|
computeService.index,
|
||||||
algoDefinition,
|
algoDefinition,
|
||||||
null, // no marketplace fee
|
null, // no marketplace fee
|
||||||
computeAddress // CtD is the consumer of the dataset
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
datasetWithBogusProvider
|
||||||
)
|
)
|
||||||
assert(order === null, 'Order should be null')
|
assert(order === null, 'Order should be null')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -1585,7 +1634,9 @@ describe('Compute flow', () => {
|
|||||||
algoDefinition,
|
algoDefinition,
|
||||||
output,
|
output,
|
||||||
`${computeService.index}`,
|
`${computeService.index}`,
|
||||||
computeService.type
|
computeService.type,
|
||||||
|
undefined,
|
||||||
|
ddo
|
||||||
)
|
)
|
||||||
assert(response === null || response === undefined, 'Compute error')
|
assert(response === null || response === undefined, 'Compute error')
|
||||||
})
|
})
|
||||||
@ -1604,7 +1655,12 @@ describe('Compute flow', () => {
|
|||||||
})
|
})
|
||||||
it('Bob should fail to stop a fake compute job on a bogus provider', async () => {
|
it('Bob should fail to stop a fake compute job on a bogus provider', async () => {
|
||||||
const jobid = '1234'
|
const jobid = '1234'
|
||||||
const response = await ocean.compute.stop(bob, datasetWithBogusProvider.id, jobid)
|
const response = await ocean.compute.stop(
|
||||||
|
bob,
|
||||||
|
datasetWithBogusProvider.id,
|
||||||
|
jobid,
|
||||||
|
ddo
|
||||||
|
)
|
||||||
assert(response === null || response === undefined, 'Invalid response')
|
assert(response === null || response === undefined, 'Invalid response')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user