Informations in asset disapear after a put - hyperledger-composer

I work in hyperledger composer. On model cto I define
- an asset Child which contains a list of vaccine. This asset is defined by :
asset Child identified by childId {
o String childId
o String name
o String surname
o DateTime dateOfBirth
o Vaccin[] vaccins optional
--> Parent hasparent
--> Doctor hasdoctor
}
an asset defVaccin which contains some definitions of vaccines. A vaccin is defined by :
asset defVaccin identified by vaccinId {
o String vaccinId
o String diseases
o Integer timeFirst
o Integer timeSecond optional
o Integer timeThird optional
o Integer timeFourth optional
o Integer timeFifth optional
}
To create/add vaccines in this list, I use a transaction "vaccinate" which is defined like this on the model cto :
transaction Vaccinate {
--> Child inchild
--> defVaccin aboutvaccin
o DateTime dateOfVaccin
}
And like this on logic.js
function vaccinate(vaccinate) {
var factory = getFactory();
var vaccin = factory.newConcept('vaccinspace', 'Vaccin', vaccinate.aboutvaccin.vaccinId); // create vaccin concept
// define value of concept's properties
vaccin.vaccinId = vaccinate.aboutvaccin.vaccinId;
vaccin.dateOfVaccin = vaccinate.dateOfVaccin;
// add this vaccine at the list of child's vaccines
vaccinate.inchild.addArrayValue("vaccins", vaccin)
return getAssetRegistry('vaccinspace.Child')
.then (function (assetRegistry) {
return assetRegistry.update(vaccinate.inchild); // update the list of child's vaccines
});
}
This works fine, i have all my vaccines in my list. But if i modifie my child or my vaccin (just do a put to change child's name for exemple), i have an empty list after.
Does someone knows why my informations "disapear" from my list ? How can i change this?

see example here -> https://github.com/hyperledger/composer-sample-networks/blob/master/packages/carauction-network/lib/logic.js#L89
try
// add this vaccine to the list of child's vaccines array of concepts
vaccinate.inchild.vaccins.push(vaccin);
instead of
// add this vaccine at the list of child's vaccines
vaccinate.inchild.addArrayValue("vaccins", vaccin);

Related

Transaction function return type in Hyperledger Composer

I am using the #return(MyConcept) in transaction function defination i.e in cto file.
Based on certain conditions in the transaction fuunction, I want the return type to be dynamic i.e sometimes it may return MyConcept1 and sometimes MyConcept2 or sometimes even null.
How can I achieve this?
use a 'master' Concept to hold the other optional Concepts
example:
participant Publisher identified by id {
o String id
}
asset myAsset identified by id {
o String id
o String value
}
concept MyConcept1 {
o String value
}
concept MyConcept2 {
o String value optional
}
concept MyConcept {
o MyConcept1 myc1 optional
o MyConcept2 myc2 optional
}
#returns(MyConcept)
transaction myTransaction {
--> myAsset
etc
}
my Transaction could return anything in that Concept you set
eg.
/**
* Handle a transaction that returns a concept.
* #param {org.sample.MyTransaction} transaction The transaction.
* #returns {org.sample.MyConcept} The concept.
* #transaction
*/
async function myTransaction(transaction) {
// other stuff
const factory = getFactory();
// assign values
var conceptData1 = factory.newConcept('org.sample', 'MyConcept1');
conceptData1.value = transaction.myAsset.value; // etc
//
// return master (you define myConceptdata) based on what was set .. some of which could be blank
return myConceptdata;
}

hyperledger composer: not able to access attributes of object returned from query

Within a transaction processor function within my hyperledger composer application I used the following line of code (in order to get the patient with the email address 'adam#gmail.com'):
let result = await query('selectPatientByEmail', {
"email": "adam#gmail.com"
});
The query is defined in the queries.qry file as follows:
query selectPatientByEmail {
description: "Select the patient with the given email address"
statement:
SELECT org.comp.app.Patient
WHERE (email == _$email) }
A Patient is defined as follows in the model.cto file:
participant Patient identified by id {
o String id
o String firstName
o String lastName
o String email
}
The problem is this: when I try to access the id of the returned patient, this is not possible. That is, result.id is "undefined"
How can I access the id of the patient returned?
This question is related to the following question:
how to define BusinessNetworkConnection in hyperledger-composer transaction processor?
remember that the results are going to be an array of JS objects.
So you would use a for loop to step through the results - then you can acccess any attribute of the object (per your model) eg.
let result = await query('selectPatientByEmail', {
"email": "adam#gmail.com"
});
for (var n = 0; n < result.length; n++) {
console.log("Patient Id is " + result[n].id );
// alternative : console.log("Patient Id is " + result[n].getIdentifier());
console.log("Patient Email is " + result[n].email);
}
The participant Patient that you have defined their is no attribute with name email. So The query selectPatientByEmail will always return the Empty Object {}. When you are calling result.id equivalent to {}.id which will be undefined

Hyperledger transaction should have been denied, but went to default rule

I am trying to run my test for my chaincode and this is what I am getting.
return useIdentity(aliceIdentity)
.then(() => {
// Submit the transaction.
const transaction = factory.newTransaction('com.james.demo', 'UpdateAppointment');
transaction.asset = factory.newRelationship('com.james.demo', 'Appointment', '2', '11/2/2017', '08:19','new','3');
transaction.newValue = '50';
return businessNetworkConnection.submitTransaction(transaction);
})
.should.be.rejectedWith(/does not have .* access to resource/);
I get this:
AssertionError: expected promise to be rejected with an error matching /does not have .* access to resource/ but it was fulfilled with undefined
This is what my code looks like:
/**
* Sample transaction processor function.
* #param {com.james.demo.UpdateAppointment} tx The sample transaction instance.
* #transaction
*/
function UpdateAppointment(tx, patient, doctor, origAppointment) {
// Save the old value of the asset.
var oldValue = tx.asset.value;
// Update the asset with the new value.
tx.asset.value = tx.newValue;
// Get the asset registry for the asset.
return getAssetRegistry('com.james.demo.Appointment')
.then(function (assetRegistry) {
// Update the asset in the asset registry.
return assetRegistry.update(tx.asset);
})
.then(function () {
console.log(JSON.stringify(tx));
});
}
This is the rule:
rule DoctorHasFullAccessToTheirAssets {
description: "Allow all participants full access to their assets"
participant(p): "com.james.demo.Doctor"
operation: ALL
resource(r): "com.james.demo.Appointment"
condition: (r.doctor.getIdentifier() === p.getIdentifier())
action: ALLOW
}
This is appointment
asset Appointment identified by appointmentId {
o String appointmentId
o String appointmentDate optional
o String appointmentTime optional
o String status optional
--> Doctor doctor optional
--> Patient owner optional
o String value optional
}
And doctor:
participant Doctor identified by npiId {
o String npiId
o String firstName
o String lastName
}
Inside UpdateAppointment I have a console.log command that isn't being executed, it appears. so I am thinking that my function isn't actually being executed.
Having multiple parameters in my UpdateAppointment, is that incorrect?
How do I get this test to pass?
I didn't see your modeled transaction ie com.james.demo.UpdateAppointment in your post. But you would just pass the UpdateAppointment transaction instance so:
function UpdateAppointment(tx) {
...
}
Then reference your patient, doctor (participants) and appointments (Asset listing, for example) via relationships in your transaction definition.
Check out this sample-network logic https://github.com/hyperledger/composer-sample-networks/blob/master/packages/trade-network/lib/logic.js and also how the relationships are modeled in the transaction (ie may be a similar thing for you: relationship of Appointment to: Patient (appointee and participant), doctor (participant in the appointment), origAppointment (Patient can have one-to-many relationship with appointments as a listing?). Anyway, you know your use case better. Point being, your transaction instance has the means to know who the participants are and what the asset instance is, when the transaction logic is being executed, via the Transaction Processor function - see more here https://hyperledger.github.io/composer/reference/js_scripts.html
hope this helps..

Handling Related objects in Hyperledger composer

Model:
namespace org.acme.model
enum CustomerSegment {
o P
o E
}
asset Account identified by Account_No {
o String Account_No
--> Customer Account_Holder
o String Account_Type
o Integer Balance
}
participant Customer identified by Customer_ID {
o String Customer_ID
o String First_Name
o CustomerSegment Customer_Segment
}
transaction UpdateCustomerSegment{
--> Account A
--> Customer C
}
transaction changeBalance{
--> Account A
o Integer newBalance
}
event UCS_Event{
-->Account A
o String oldsegment
o String newsegment
}
event BalanceChangEvent{
-->Account A
o Integer oldbalance
o Integer newbalance
}
Script:
/**
* Place an order for a vehicle
* #param {org.acme.model.UpdateCustomerSegment} ucs - the transaction that calculated Customer Segment
* #transaction
*/
function UpdateCustomerSegment(ucs)
{
var CustomerID=ucs.C.Customer_ID;
var oldCS=ucs.C.Customer_Segment;
if(ucs.A.Balance>3000)
ucs.C.Customer_Segment="E"
else
ucs.C.Customer_Segment="P"
var cust = getParticipantRegistry('org.acme.model.Customer')
.then(function(participantRegistry){
return participantRegistry.update(ucs.C);
})
.then(function(){
//Emit Event
var event_g= getFactory().newEvent('org.acme.model','UCS_Event');
event_g.A=ucs.A;
event_g.oldsegment=oldCS;
event_g.newsegment=ucs.C.Customer_Segment
emit(event_g);
})
return cust;
}
What I need to do is -
Pass only the Account No. to the transaction.
the transaction should fetch the appropriate Customer - where customer ID is same as Account's Account Holder, and update the Customer's Customer Segment
which I am unable to do. is it even doable. I am new, so not sure.
in the above transaction I am passing both the Account No. and Customer ID
2nd Question
Can we update a Participant & Asset both in the same transaction ? if yes how.
3rd Question:
how to call one transaction from within another.
Your model looks good, now you need to write a Transaction Processor function that subscribes to UpdateCustomerSegment transaction and that implements your logic to change the customer segment of the customer, and then persist the customer.
The basic-sample-network includes a similar simple transaction processor:
/**
* Sample transaction processor function.
* #param {org.acme.sample.SampleTransaction} tx The sample transaction instance.
* #transaction
*/
function sampleTransaction(tx) {
// Save the old value of the asset.
var oldValue = tx.asset.value;
// Update the asset with the new value.
tx.asset.value = tx.newValue;
// Get the asset registry for the asset.
return getAssetRegistry('org.acme.sample.SampleAsset')
.then(function (assetRegistry) {
// Update the asset in the asset registry.
return assetRegistry.update(tx.asset);
})
.then(function () {
// Emit an event for the modified asset.
var event = getFactory().newEvent('org.acme.sample', 'SampleEvent');
event.asset = tx.asset;
event.oldValue = oldValue;
event.newValue = tx.newValue;
emit(event);
});
}

Build tree structure from the records

I query database for records in structure as follows
ID | Term | ParentID
In C# code I have following class
public class Tree
{
public string Id { get; set; }
public string Term { get; set; }
public string ParentId { get; set; }
public int Level { get; set; }
public IList<Tree> ChildItems { get; set; }
}
Query returns 5 000 000 records.
I need to build tree of Tree items and populate it.
First at all, I select all items where ParentID is null, and then for every element search parent (if parent doesn't exist I build parent of the parent and so on) and build tree using recursion.
I'm not happy with my algorithm because It takes more than 5 minutes.
Please, let me some advice how to do that, what to use and so on.
This is how the code is now implemented:
private string Handle2(List<Tree> originalTree)
{
IList<Tree> newTree = new List<TreeTest.Models.Tree>();
IList<Tree> treeWithoutParents = originalTree.Where(x => String.IsNullOrEmpty(x.ParentID)).OrderBy(x => x.Term).ToList();
foreach(Tree item in treeWithoutParents)
{
Tree newItem = new Tree { Id = item.ID, Term = item.Term, ParentId = item.ParentID, Level = 0 };
newTree.Add(newItem);
InsertChilds(newItem, originalTree, 0);
}
return "output";
}
private void InsertChilds(Tree item, List<Tree> origTree, int level)
{
++level;
IList<Tree> childItems = origTree.Where(x => x.ParentID == item.Id).ToList();
origTree.RemoveAll(x => x.ParentID == item.Id);
foreach (Tree i in childItems)
{
origTree.Remove(i);
}
foreach (Tree tItem in childItems)
{
if (item.ChildTree == null)
{
item.ChildTree = new List<TreeTest.Models.Tree>();
}
Tree itemToAdd = new Tree { Id = tItem.ID, Term = tItem.Term, ParentId = tItem.ParentID, Level = level };
this.InsertChilds(itemToAdd, origTree, level);
item.ChildTree.Add(itemToAdd);
}
}
Try using a map (C# Dictionary) of ID (string, although I'm curious why that isn't int) to node (Tree object) to store your tree nodes.
This would allow you to get the node corresponding to an ID with expected O(1) complexity, rather than your current O(n) complexity.
Beyond that, I suggest you rethink your approach a bit - try to write code which involves you only going through the input data once, just use a single Dictionary - if the parent doesn't exist yet, you could just create a filler-item for the parent, which has its members populated only when you get to that item.
I would use a dictionary (hash table) to make this faster. Here is my algorithm in pseudocode:
- create a dictionary mapping ID to IList<Tree> // mapping a node to its children
- create Queue<string,string> of IDs //item (3,5) in the queue corresponds to a node with ID=3 that has a parent with ID=5
- initialize the queue with all the codes with no parent
- List<Tree> withoutParent = dictionary[null]
- for each item in withoutParent:
- add (item.Id, null) to the queue
- while queue is not empty:
- (id,pid) = delete an item from the queue
- make a new node t
- t.Id = id
- t.parentId = pid
- t.ChildItems = dictionary[id]
- for each child in t.ChildItems:
- add (child.Id, id) to the queue
is the column ID a unique identifier. If it is then you can try the following. Instead of using a List, use a Set or a hashmap. This is because if a parent has too many child, lookup in a list can slow down your operations. If you use a Set, you can do a quick lookup and you can also do a quick addition of your elements.
Also, can you check how much time an order by clause will take . This might really help you speed up your process. If ID is a clustered index, you will get a fast sort by(as the data is already sorted) , else your query will still use the same index
When a parent does not exist , you are creating a parent of a parent. I would try to avoid that. What you can do is in case a child's parent does not exist in the tree, add it to a separate list. After you have gone through the original list , make a second pass to find orphaned elements. The advantage is that you do not need to resize your tree every time you create a parent of parent and then find out that the parent was just at the end of the list

Resources