How to update the relationship values inside transaction processor functions? - hyperledger-fabric

I have a model file org.acme.interm.container.cto which looks something like this :
namespace org.acme.interm.container
asset Container identified by containerId {
o String containerId regex=/[A-Z][A-Z][0-9][0-9][0-9]-[0-9][0-9]-[0-3][0-9]-[0-9][0-9]/
o String containerNumber
--> Truck truck optional
}
asset Truck identified by truckId {
o String truckId
o Ownership ownershipType default="LEASED"
o Integer normalWeight range = [,100]
o Integer fragileWeight range = [,50]
}
enum Ownership {
o LEASED
o OWNED
}
transaction AssignTruck {
o String containerId
o String truckId
}
event TruckAssigned {
o String containerId
o String truckId
}
transaction LoadContainer {
o String containerId
--> Truck truck
o Integer fragileWeight
o Integer normalWeight
}
I am assigning the Truck relation to the container asset in the following transaction :
function AssignTruck(containerTruckData) {
console.log("ASSIGN TRUCK CALLED!");
var containerRegistry={}
return getAssetRegistry('org.acme.interm.container.Container').then(function(registry){
containerRegistry = registry
return containerRegistry.get(containerTruckData.containerId);
}).then(function(container){
console.log("Got Container",container);
if(!container) throw new Error("Container : "+containerTruckData.containerId," Not Found!!!");
var factory = getFactory();
var relationship = factory.newRelationship('org.acme.interm.truck','Truck',containerTruckData.truckId);
container.truck = relationship;
return containerRegistry.update(container);
}).then(function(){
// Successful update
var event = getFactory().newEvent('org.acme.interm.container', 'TruckAssigned');
event.containerId = containerTruckData.containerId;
event.truckId = containerTruckData.truckId;
emit(event);
}).catch(function(error){
throw new Error(error);
});
}
Now, how can i write the loadContainer transaction, where the existing values of normalWeight and fragileWeight properties of the container(for given containerId) should be added to the newly passed fragileWeight and normalWeight
values?

Firstly unless each container has a weight variable, it is impossible to track the individual weight. Even if you keep updating the weight in the trucks, you will know the collective weight of containers which is not enough. Especially if you want to unload containers. I would suggest this approach
asset Container identified by containerId {
o String containerId regex=/[A-Z][A-Z][0-9][0-9][0-9]-[0-9][0-9]-[0-3][0-9]-[0-9][0-9]/
o String containerNumber
o Integer normalWeight range = [,100]
o Integer fragileWeight range = [,50]
--> Truck truck optional
}
asset Truck identified by truckId {
o String truckId
o Ownership ownershipType default="LEASED"
o Integer totalNormalWeight range default=0
o Integer totalFragileWeight range default=0
--> Container containersLoaded[] optional
}
So this will let you track weight per container and then add them to get the total in truck. The containersLoaded variable will let you keep track of containers loaded. An advantage of this is, you can accurately reduce the weight if a container is unloaded or split (which I assume will be part of the user flow)
function AssignTruck(containerTruckData) {
var myContainer; // store data from the promise
var myTruck;
console.log("ASSIGN TRUCK CALLED!");
var containerRegistry={}
var truckRegistry;
return getAssetRegistry('org.acme.interm.container.Container').then(function(registry){
containerRegistry = registry
return containerRegistry.get(containerTruckData.containerId);
}).then(function(container){
console.log("Got Container",container);
myContainer = container;
if(!container) throw new Error("Container : "+containerTruckData.containerId," Not Found!!!");
var factory = getFactory();
var relationship = factory.newRelationship('org.acme.interm.truck','Truck',containerTruckData.truckId);
container.truck = relationship;
return containerRegistry.update(container);
}).then(function() {
return getAssetRegistry('org.acme.interm.truck.Truck');
}).then(function (truckRegistry) {
truckRegistry = truckRegistry;
return truckRegistry.get(containerTruckData.truckId);
}).then(function (truck) {
truck.totalNormalWeight += myContainer.normalWeight;
truck.totalFragileWeight += myContainer.fragileWeight;
if (!truck.containersLoaded) {
truck.containersLoaded = [];
} else {
truck.containersLoaded.push(factory.newRelationship('org.acme.interm.container', 'Container', myContainer.containerId))
};
return truckRegistry.update(truck)
}).then(function(){
// Successful update
var event = getFactory().newEvent('org.acme.interm.container', 'TruckAssigned');
event.containerId = containerTruckData.containerId;
event.truckId = containerTruckData.truckId;
emit(event);
}).catch(function(error){
throw new Error(error);
});
}
This should work for you. Now you can query the truck and get total weight of all containers by checking the totalNormalWeight and totalFragileWeight variables. You can also check the containersLoaded array to get the list of the containers. To get the individual weight of the a container, either you can query the container and get its weight or get it directly from the containersLoaded variable.
When the containers need to be removed/ unloaded from the truck, you can get their individual weight and subtract from the total, and pop the relationship from the containersLoaded.

Related

Hyperledger fabric composer transaction not working

I'm trying add this transaction named placeOrder i want to add a Customer participant
before creating Order asset and map its relationship with the Order asset while processing this transaction. But I'm getting customer not defined error. Can anybody help? Thanks.
My models
namespace org.isn.customer
participant Customer identified by email {
o String firstName
o String lastName
o String email
o String password
}
enum Status{
o ACTIVE
o OFF_THE_ROAD
}
asset Vehicle identified by serial {
o String brand
o String model
o String color
o Status status
o Double price
o String serial
}
asset Order identified by orderId{
o String orderId
o Vehicle item
--> Customer customer
}
transaction PlaceOrder {
o String orderId
--> Vehicle item
o Customer customer
}
script.js
/**
* #param {org.isn.shop.PlaceOrder}orderRequest
* #transaction
*/
async function placeOrder(orderRequest){
const factory = getFactory();
const customerRegistry = await getParticipantRegistry("org.isn.customer.Customer");
const customerExists = await customerRegistry.exists(orderRequest.customer.email);
if(!customerExists){
const customer = factory.newResource("org.isn.customer","Customer",orderRequest.customer.email);
customer.firstName = orderRequest.customer.firstName;
customer.lastName = orderRequest.customer.lastName;
customer.email = orderRequest.customer.email;
customer.password = orderRequest.customer.password;
await customerRegistry.add(customer);
}else{
const customer = await customerRegistry.get(orderRequest.customer.email);
}
const order = await factory.newResource("org.isn.shop","Order",orderRequest.orderId);
order.customer = customer.getIdentifier();
order.item = orderRequest.item;
const orderRegistry = await getAssetRegistry("org.isn.shop.Order");
await orderRegistry.add(order);
const PlaceOrderEvent = factory.newEvent("org.isn.shop","PlaceOrderEvent");
placeOrderEvent.order = order;
emit(placeOrderEvent);
}
o Customer customer needs to become --> Customer customer then you will be able to reference orderRequest.customer.firstName etc also not sure you need order.customer = customer.getIdentifier(); might need to become order.customer.email = customer.getIdentifier();
The following version of the Model and the JS will work for a new and existing customer:
namespace org.isn.customer
participant Customer identified by email {
o String firstName
o String lastName
o String email
o String password
}
enum Status{
o ACTIVE
o OFF_THE_ROAD
}
asset Vehicle identified by serial {
o String brand
o String model
o String color
o Status status
o Double price
o String serial
}
asset Order identified by orderId{
o String orderId
--> Vehicle item
--> Customer customer
}
transaction PlaceOrder {
o String orderId
--> Vehicle item
o Customer customer
}
/**
* #param {org.isn.customer.PlaceOrder}orderRequest
* #transaction
*/
async function placeOrder(orderRequest){
const factory = getFactory();
const customerRegistry = await getParticipantRegistry("org.isn.customer.Customer");
const customerExists = await customerRegistry.exists(orderRequest.customer.email);
if(!customerExists){
var customer = factory.newResource("org.isn.customer","Customer",orderRequest.customer.email);
customer.firstName = orderRequest.customer.firstName;
customer.lastName = orderRequest.customer.lastName;
customer.email = orderRequest.customer.email;
customer.password = orderRequest.customer.password;
await customerRegistry.add(customer);
}else{
var customer = await customerRegistry.get(orderRequest.customer.email);
}
const order = await factory.newResource("org.isn.customer","Order",orderRequest.orderId);
order.customer = factory.newRelationship("org.isn.customer","Customer",customer.getIdentifier());
order.item = orderRequest.item;
const orderRegistry = await getAssetRegistry("org.isn.customer.Order");
await orderRegistry.add(order);
}
Please note the following:
In the Order asset I have changed the o Vehicle to be --> Vehicle item to match up with the Transaction.
I have also used var customer in both the if and else code.
(To make mine work I had to use the same namespace, but perhaps you had separate .cto files)

azure table storage pagination for request 10 items each time

Basically I am trying to get pagination working when requesting entities of azure table storage. i.e. Press next button gets the next 10 entities & Press previous button gets the previous 10 entities. A relatively close example Gaurav Mantri's Answer. But my question is how do I get the nextPartitionKey and nextRowKey from a HTML button attribute and store in to a array/list in order to keep track of current page so I can get the next/previous items?Code example would be very appreciated.
Thanks!
This is something I have right now which gets a range of data based on pageNumber request
private async Task<List<UserInfo>> queryPage(CloudTable peopleTable, string item, int pageNumber)
{
// Construct the query operation for all customer entities
TableQuery<CustomerEntity> query = new TableQuery<CustomerEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, item));
// Print the fields for each customer.
TableContinuationToken token = null;
//TodoItem data = new TodoItem();
List<UserInfo> data = new List<UserInfo>();
do
{
TableQuerySegment<CustomerEntity> resultSegment = await peopleTable.ExecuteQuerySegmentedAsync(query, token);
token = resultSegment.ContinuationToken;
foreach (CustomerEntity entity in resultSegment.Results)
{
data.Add(new UserInfo
{
// add data
});
}
} while (token != null);
//get a subset of all entity
List<UserInfo> sublist = data.GetRange(0, pageNumber);
return sublist;
}
Managed to solved the problem under Gaurav's help.
Here is the code, not perfect but works.
private async Task<List<UserInfo>> queryPage(CloudTable peopleTable, string item, string NextPartitionKey , string NextRowKey, int itemNumber)
{
// Construct the query operation for all customer entities
TableQuery<CustomerEntity> query = new TableQuery<CustomerEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, item)).Take(itemNumber);
// Print the fields for each customer.
List<UserInfo> data = new List<UserInfo>();
Tabletoken.NextPartitionKey = NextPartitionKey;
Tabletoken.NextRowKey = NextRowKey;
TableQuerySegment<CustomerEntity> resultSegment = await peopleTable.ExecuteQuerySegmentedAsync(query, Tabletoken);
Tabletoken = resultSegment.ContinuationToken;
foreach (CustomerEntity entity in resultSegment.Results)
{
data.Add(new UserInfo
{
//add data
});
}
return data;
}
private TableContinuationToken Tabletoken = new TableContinuationToken();
and declare it use a tuple.
Tuple<List<UserInfo>, string, string > tuple =
new Tuple<List<UserInfo>, string, string>(data, Tabletoken.NextPartitionKey, Tabletoken.NextRowKey);

Return a set of objects from a class

I have a method that adds a new item to an EF table, then queries back the table to return a subset of the table. It needs to return to the caller a set of "rows", each of which is a set of columns. I'm not sure how to do this. I have some code, but I think it's wrong. I don't want to return ONE row, I want to return zero or more rows. I'm not sure what DataType to use... [qryCurrentTSApproval is an EF object, referring to a small view in SS. tblTimesheetEventlog is also an EF object, referring to the underlying table]
Ideas?
private qryCurrentTSApproval LogApprovalEvents(int TSID, int EventType)
{
using (CPASEntities ctx = new CPASEntities())
{
tblTimesheetEventLog el = new tblTimesheetEventLog();
el.TSID = TSID;
el.TSEventType = EventType;
el.TSEUserName = (string)Session["strShortUserName"];
el.TSEventDateTime = DateTime.Now;
ctx.tblTimesheetEventLogs.AddObject(el);
ctx.AcceptAllChanges();
var e = (from x in ctx.qryCurrentTSApprovals
where x.TSID == TSID
select x);
return (qryCurrentTSApproval)e;
}
}
Change your method return type to a collection of qryCurrentTSApproval
private List<qryCurrentTSApproval> LogApprovalEvents(int TSID, int EventType)
{
using (CPASEntities ctx = new CPASEntities())
{
// some other existing code here
var itemList = (from x in ctx.qryCurrentTSApprovals
where x.TSID == TSID
select x).ToList();
return itemList;
}
}

Sequence Number for Id

I want to configure my Mongo DB to create sequence number for an Id column. Ex. It has to start from 1001 and increase by 1 automatically when I insert next row. I have my schema definitions as part of Node.JS how to add this configuration in Node schema?
MongoDB doesn't support this out of the box. The way I've implemented this (albeit in C#) is to create a "Sequence" collection with a key and a next number. You can atomically increment and return the next number then use this as the id in your collection.
This is a C# function, using the findandmodify mongodb command to fetch and update a sequence number for a given "key".
public long GetNextSequenceNumber(string name, string key)
{
var update = new BsonDocument(new BsonElement("$inc", new BsonDocument(new BsonElement("SequenceNumber", 1))));
var query = new BsonDocument("_id", key);
var command = new CommandDocument {
{ "findandmodify" , name },
{ "query", query},
{ "update" , update},
{ "new" , true},
};
var res = Db.RunCommand(command);
if (res.Response["value"] != BsonNull.Value)
{
var o = BsonSerializer.Deserialize<Sequence>(res.Response["value"].ToBsonDocument());
return o.SequenceNumber;
}
else
{
var o = new Sequence() { Id = key, SequenceNumber = 0 };
Db.GetCollection(name).Insert<Sequence>(o);
return o.SequenceNumber;
}
}
and the Sequence model:
public class Sequence
{
public string Id { get; set; }
public long SequenceNumber { get; set; }
}
The sequence documents look like:
{
_id : 'mykey',
SequenceNumber : NumberLong(1234)
}
If you need converting it to javascript please ask.
Hope that helps.

Detect Change in EntityFrameWork

In my current project I need write in a table all values are changed in the application.
Ex. the guy update the UserName, I need put in a table UserName old value "1" new value "2".
I tried use the ObjectStateEntry but this return all fields. I think the FW return all because my code.
public USER Save(USER obj)
{
using(TPPTEntities db = new TPPTEntities())
{
db.Connection.Open();
USER o = (from n in db.USERs where n.ID == obj.ID select n).FirstOrDefault();
if (o == null)
{
o = new USER()
{
BruteForce = 0,
Email = obj.Email,
IsBlock = false,
LastLogin = DateTime.Now,
Name = obj.Name,
UserName = obj.UserName,
UserPassword = new byte[0],
};
db.AddToUSERs(o);
}
else
{
o.Email = obj.Email;
o.Name = obj.Name;
o.UserName = obj.UserName;
}
db.SaveChanges();
db.Connection.Close();
}
return obj;
}
A way to get old and new values is this:
var ose = this.ObjectStateManager.GetObjectStateEntry(o.EntityKey);
foreach (string propName in ose.GetModifiedProperties())
{
string.Format("Property '{0}', old value: {1}, new value: {2}",
propName, ose.OriginalValues[propName], ose.CurrentValues[propName]);
}
This is pretty useless, of course, but I'm sure you'll know what to do in the foreach loop to store the changes.
Is this a WCF Service? In that case, the changes will probably never come trough since changes to the Object Graph are made where the Object Context is not available. Consider using Self-Tracking Entities

Resources