I am trying to develop a chaincode for Hyperledger Fabric 1.4 using the IBM Blockchain Platform plugin for Visual Studio Code and the fabric-contract-api v1.4.2. In this situation, I am facing some problems when trying to use interfaces from my chaincode methods. This is the error:
Error: Type not properly specified for parameter myAssetConfig, can not process pure Object types
The asset I am using is called MyAsset. This is the declaration of that element:
#Object()
export class MyAsset {
#Property()
public propertyA: string;
#Property()
public propertyB: string;
#Property()
public propertyC?: string;
constructor(myAssetConfig: IMyAssetConfig) {
this.propertyA = myAssetConfig.propertyA;
this.propertyB = myAssetConfig.propertyB;
if (myAssetConfig.hasOwnProperty('propertyC')) {
this.propertyC = myAssetConfig.propertyC;
}
}
}
Apart from it, this the content of types/index.d.ts (I am using the flag #Object here but I am not exactly sure if I should and why/why not):
#Object
export interface IMyAssetConfig {
propertyA: string;
propertyB: string;
propertyC?: string;
}
export type MyAssetId = string;
Finally, this is the content of myasset-contract.ts
#Info({title: 'MyAssetContract', description: 'My MyAsset Contract'})
export class MyAssetContract extends Contract {
#Transaction(false)
#Returns('boolean')
public async myAssetExists(ctx: Context, myAssetId: MyAssetId): Promise<boolean> {
const buffer = await ctx.stub.getState(myAssetId);
return (!!buffer && buffer.length > 0);
}
#Transaction()
public async createMyAsset(ctx: Context, myAssetConfig: IMyAssetConfig): Promise<MyAssetId> {
const myAssetId: MyAssetId = myAssetConfig.shippingCompanyId + '-' + this.generateInternMyAssetId(ctx);
const exists = await this.myAssetExists(ctx, myAssetId);
if (exists) {
throw new Error(`The myAsset ${myAssetId} already exists`);
}
const myAsset = new MyAsset(myAssetConfig);
const buffer = Buffer.from(JSON.stringify(myAsset));
await ctx.stub.putState(myAssetId, buffer);
return myAssetId;
}
#Transaction(false)
#Returns('MyAsset')
public async readMyAsset(ctx: Context, myAssetId: MyAssetId): Promise<MyAsset> {
const exists = await this.myAssetExists(ctx, myAssetId);
if (!exists) {
throw new Error(`The myAsset ${myAssetId} does not exist`);
}
const buffer = await ctx.stub.getState(myAssetId);
return JSON.parse(buffer.toString()) as MyAsset;
}
#Transaction()
public async splitMyAsset(ctx: Context, myAssetId: MyAssetId, children: IMyAssetConfig[]): Promise<MyAssetId[]> {
// REMOVED because it is actually irrelevant to the problem and makes the post too long.
return [];
}
}
Of course, this is all anonymized and reduced but I think the problem is clear enough. I can not use IMyAssetConfig as I type for the parameter myAssetConfig but there is no problem if I use string. I could understand till some point that fabric-contract-api does not accept Objects as parameters. However, if I comment all the code of createMyAsset I get no errors, and I am also using an object in splitMyAsset and I have no problem there.
Can anyone explain me what this is happening? The problems I get happen when I try to instantiate the chaincode/run tests using npm test.
Thank you very much.
Related
I am having a small question about NestJS. In my code, there is a service which looks something like:
`
import { Inject, Injectable } from '#nestjs/common';
import neo4j, { Driver, int, Result, Transaction } from 'neo4j-driver';
import { Neo4jConfig } from './neo4j-config.interface';
import { NEO4J_CONFIG, NEO4J_DRIVER } from './neo4j.constants';
#Injectable()
export class Neo4jService {
constructor(
#Inject(NEO4J_CONFIG) private readonly config: Neo4jConfig,
#Inject(NEO4J_DRIVER) private readonly driver: Driver,
) {}
onApplicationBootstrap() {
console.log('Hello');
}
getDriver(): Driver {
return this.driver;
}
getConfig(): Neo4jConfig {
return this.config;
}
int(value: number) {
return int(value);
}
beginTransaction(database?: string): Transaction {
const session = this.getWriteSession(database);
return session.beginTransaction();
}
getReadSession(database?: string) {
return this.driver.session({
database: database || this.config.database,
defaultAccessMode: neo4j.session.READ,
});
}
getWriteSession(database?: string) {
return this.driver.session({
database: database || this.config.database,
defaultAccessMode: neo4j.session.WRITE,
});
}
read(
cypher: string,
params?: Record<string, unknown>,
databaseOrTransaction?: string | Transaction,
): Result {
if (databaseOrTransaction instanceof Transaction) {
return (<Transaction>databaseOrTransaction).run(cypher, params);
}
const session = this.getReadSession(<string>databaseOrTransaction);
return session.run(cypher, params);
}
write(
cypher: string,
params?: Record<string, unknown>,
databaseOrTransaction?: string | Transaction,
): Result {
if (databaseOrTransaction instanceof Transaction) {
return (<Transaction>databaseOrTransaction).run(cypher, params);
}
const session = this.getWriteSession(<string>databaseOrTransaction);
return session.run(cypher, params);
}
private onApplicationShutdown() {
console.log('Goodbye')
return this.driver.close();
}
}
`
Then in my main.ts file I have this method called:
`
await app.listen(port);
`
As you can see my service does not implement neither onApplicationBootstrap nor onApplicationShutdown.
How does it come that those methods still get invoked? Should I implement onApplicationBootstrap and onApplicationShutdown or not?
As you can also see I' d like that my onApplicationBootstrap is a private method which would not be possible if I implement the interface.
So, I would like to ask you:
Why the two lifecycle methods get called event without implementing the interface?
Should I implement those interfaces at all or just go on and use the methods which would allow me to define them as private?
I expected those methods to not work without implementing the interfaces
The Typescript interface is there to help us as devs. It doesn't exist at runtime, there's no information about it, so the only thing Nest can do is just check "Hey, does this class have the onModuleInit method?" If yes, add it to a list of classes to call onModuleInit. Do the same with the other lifecycle methods.
The interfaces aren't explicitly necessary, but they do give us devs a better idea of the class by just looking at the export class... line because we can see what is implemented/extended.
I am developing a chaincode written in Typescript using fabric-contract-api and the IBM Blockchain Platform plugin for Visual Studio Code. My asset is called Order and, though tests pass perfectly, I am unable to instantiate it. The error I get is the following one:
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0|{ [Error: can't resolve reference Object from id Order#]
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| message: 'can\'t resolve reference Object from id Order#',
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| missingRef: 'Object',
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| missingSchema: 'Object' }
I think that the log is not clear enough and I am unable to locate the problem. I understand, as seen in other issues, that the problem is related to fabric-contract-api being unable to handle types like any or Object. However, I am not using that in my code. Everything is declared.
I have started commenting the code function by function to locate the problem and, for example, I get that error if this function is not commented:
#Transaction()
public async createAsset(ctx: Context, assetConfigStringified: string): Promise<string> {
const assetConfig: IAssetConfig = JSON.parse(assetConfigStringified);
const assetId: string = await this.generateInternAssetId(ctx);
const exists = await this.assetExists(ctx, assetId);
if (exists) {
throw new Error(`The asset ${assetId} already exists`);
}
const asset: Asset = new Asset(assetConfig);
const buffer: Buffer = Buffer.from(JSON.stringify(asset));
await ctx.stub.putState(assetId, buffer);
return assetId;
}
These are the the declarations of the interfaces used:
#Object
export interface IComplexType {
propertyA: string;
propertyB: string;
propertyC: string;
propertyD: number;
propertyE?: string;
}
#Object
export interface IAssetConfig {
propertyA: string;
propertyB: IComplexType;
propertyC: IComplexType;
propertyD: string;
propertyE?: string;
}
And this is the asset class:
#Object()
export class Asset {
#Property()
public propertyA: string;
#Property()
public propertyB: IComplexType;
#Property()
public propertyC: IComplexType;
#Property()
public propertyD: string;
#Property()
public propertyE?: string;
constructor(assetConfig: IAssetConfig) {
this.propertyA = assetConfig.propertyA;
this.propertyB = assetConfig.propertyB;
this.propertyC = assetConfig.propertyB;
this.propertyD = assetConfig.propertyD;
if (assetConfig.hasOwnProperty('propertyE')) {
this.propertyE = assetConfig.propertyE;
}
}
}
Thank you very much.
My service is designed in nodejs.
Below is my scenario
i have two controllers, one will be extending the other. there is a static function in both the controllers where in a static variable will be assigned some value.
depending on the condition of the data, im trying the make a call to the respective controller so that the static variable gets a appropriate assigned value.
Note:
The below code is just a snippet to explain the scenario and not the actual code of the application. But the order / calling / controller structure of this code snippet is exactly same. Also the listOfDept variable will be having separate business logic in the checkStart function of firstController and secondController.
// firstController.ts
firstController implements IFirstController {
private static listOfDept: string[];
static checkStart(){
firstController.listOfDept = // my logic to fill this object
}
constructor (){}
}
getRelevantData(next: (error: string, response: any) => void): void {
var myObject = firstController.listOfDept;
this.myRepository.uniqueData(myObject, next);
}
}
firstController.checkStart();
export = firstController;
//ifirstController.ts
interface IFirstController {
getRelevantData(next: (error: string, response: any) => void): void;
}
// secondController.ts
secondController extends firstController implements iSecondController {
private static listOfDept: string[];
static checkStart(){
firstController.listOfDept = ["Computer Science"];
}
constructor (){
super();
}
}
secondController.checkStart();
export = secondController;
//isecondController.ts
interface ISecondController implements ifirstController{}
//Controller calling the getRelevantData function
//middlewareController
middlewareController implements IMiddlewareController {
constructor(private firstController: IFirstController, private secondController: ISecondController) {
}
getDepData(data: any, next: (error: string, response: any) => void): void {
if(data.url = "fromParent") {
// im expecting this to make a call to checkStart() of firstController
this.firstController.getRelevantData();
} else {
// im expecting this to make a call to checkStart() of secondController
this.secondController.getRelevantData();
}
}
}
Problem faced with the above code
No matter which way the getRelevantData function is getting called, im always getting the value of listOfDept as computer science. It is never going in the checkStart function of first controller.
In general I would discourage using static methods for this kind of initialization and instead inject the required data into constructors or create factory methods for creating object with necessary data.
But, if you do want to use static properties, the problem is that you need to refer to the right parent class in the getRelevantData implementation. The class that constructed the instance can be accessed through constructor property. TypeScript does not process this scenario well, so you have to make a type cast:
// firstController.ts
class firstController implements IFirstController {
// Need to be `protected` to be accessible from subclass
protected static listOfDept: string[];
static checkStart(){
firstController.listOfDept; // my logic to fill this object
}
constructor (){}
getRelevantData(next: (error: string, response: any) => void): void {
// You need to refer to the constructor
let Class = this.constructor as typeof firstController;
var myObject = Class.listOfDept;
// the rest
}
}
firstController.checkStart();
//ifirstController.ts
interface IFirstController {
getRelevantData(next: (error: string, response: any) => void): void;
}
// secondController.ts
class secondController extends firstController implements ISecondController {
// No `listOfDept` definition here
static checkStart(){
secondController.listOfDept = ["Computer Science"];
}
constructor (){
super();
}
}
secondController.checkStart();
I am struggling to instantiate object from dynamically imported classes. Basically I have some plugins which kinda look like this:
export interface IPlugin {
compile(logEvent: LogEventInfo): string;
}
export class DatePlugin implements IPlugin {
public compile(logEvent: LogEventInfo): string {
const date: Date = new Date();
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
}
}
In another file I want to dynamically crawl a folder, load all source files and instantiate them. I saw that import(...).then() can return a loaded object however in my case it returns the class and my object creation starts looking very ugly:
public async loadPlugins(): Promise<void> {
// ...
await Promise.all(pluginFiles.map(async (pluginFile: string): Promise<void> => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
import(pluginFilePath).then((plugin: any): void => {
const obj: IPlugin = (new plugin[Object.keys(plugin)[0]]() as IPlugin;
// ...
});
}));
}
Isn't there any better way to instantiate all those classes when loading them?
import() promises aren't chained, this is a mistake similar to this case that may result in problems with error handling and race conditions.
map shares a common potential problem with this case. It's used only to provide promises to wait for them, but not actual values. Since the purpose of async function call is to get class instance, it's reasonable to map pluginFile input to obj output value if it's supposed to be stored then - or compile result if it isn't:
public async loadPlugins(): Promise<...> {
const plugins = await Promise.all(pluginFiles.map(async (pluginFile: string): Promise<IPlugin> => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
const pluginExports = await import(pluginFilePath);
// preferably pluginExports.default export to not rely on keys order
const Plugin: { new(): IPlugin } = Object.values(pluginExports)[0];
return new Plugin();
}));
...
}
The only real benefit that import provides here is that it's future-proof, it can seamlessly be used natively in Node.js with third-party ES modules (.mjs) files. Since TypeScript is used any way and uses require for ES module imports under the hood, it may be reasonable to discard asynchronous routine and use require synchronously instead of import for dynamic imports:
public loadPlugins(): <...> {
const plugins = pluginFiles.map((pluginFile: string): IPlugin => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
const pluginExports = require(pluginFilePath);
// preferably pluginExports.default export to not rely on keys order
const Plugin: { new(): IPlugin } = Object.values(pluginExports)[0];
return new Plugin();
}));
...
}
I've created a nodejs service to create/interact with databases.
class CRUDService {
private section: string;
private db: any;
constructor(section: string) {
this.section = section;
this.db = new DB('.' + section);
}
public create(data: any): Promise<string> {
...
}
public retrieve(id: string): Promise<string> {
...
}
public update(id: any, data: any): Promise<string> {
...
}
public delete(item: any): Promise<string> {
...
}
public all(): Promise<string> {
...
}
}
export {CRUDService};
And then I was using it by passing a database name to it:
this.db1 = new DatabaseService('database-one');
this.db2 = new DatabaseService('database-two');
This has been working well for me but recently I noticed that I am defining the same databases over and over again (in different modules) and figured there has got to be a better way to doing it so that the database can be defined once and shared across everything.
Is there a way I can extend my current service to define/initialize the database once and reference it?
export class DbOneService extends CRUDservice {
protected section = 'database-one';
}
And then use it like:
this.db1 = DbOneService;
I'm just a bit lost and the above doesn't seem to work.
UPDATE
By leaving the CRUDService as-is I was able to achieve what I by
import {CRUDService} from '../crud.service';
function DbOneService() {
return new CRUDService('database-one');
}
export {DbOneService};
Then I use it as follows:
import {DbOneService} from 'db/database-one';
const DbOneServiceDB = DbOneService();
DbOneServiceDB.all(){...}
Is there anything wrong with this approach?