Transaction function return type in Hyperledger Composer - 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;
}

Related

Unit Test JPA Specification's content

We have implemented filtering for a repository by using JPA's Specification as follows:
public Page<CustomerDTO> searchCustomers(SearchDto searchDto) {
var queryFilters = this.getQueryFitlers(searchDto);
var paginationAndSorting = this.getPaginationAndSorting(searchDto.getPageNumber(),
searchDto.getPageSize());
return customerRepository.findAll(queryFilters, paginationAndSorting)
.map(entity -> {
CustomerDTO dto = new CustomerDTO();
copyProperties(entity, dto);
return dto;
})
}
And here is the queryFilters() method which uses Specifications:
private Specification<Customer> getQueryFitlers(CustomerSearchSpecDTO filteringValues) {
Specification<Customer> specification = Specification.where(null);
if (isNotBlank(filteringValues.getLastname())) {
specification = specification.and(CustomerRepository.hasLastname(filteringValues.getLastname()));
}
if (isNotBlank(filteringValues.getTaxId())) {
specification = specification.and(CustomerRepository.hasTaxId(filteringValues.getTaxId()));
}
// several more fields with the same approach
return specification;
}
Since these query filters are optional depending upon if the searchField is empty or not, what we would like to do is verify that the specification contains the proper "filters".
For example, if the searchDto input contains only taxId not blank, then I want to check that the returned specification contains such a "filter / specification".
Note: grabbing a reference to the result of this.getQueryFilters() (which is the Specifications) is not a problem, we already achieved that.

Informations in asset disapear after a put

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);

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);
});
}

Explicit construction of entity type in query is not allowed [duplicate]

Using Linq commands and Linq To SQL datacontext, Im trying to instance an Entity called "Produccion" from my datacontext in this way:
Demo.View.Data.PRODUCCION pocoProduccion =
(
from m in db.MEDICOXPROMOTORs
join a in db.ATENCIONs on m.cmp equals a.cmp
join e in db.EXAMENXATENCIONs on a.numeroatencion equals e.numeroatencion
join c in db.CITAs on e.numerocita equals c.numerocita
where e.codigo == codigoExamenxAtencion
select new Demo.View.Data.PRODUCCION
{
cmp = a.cmp,
bonificacion = comi,
valorventa = precioEstudio,
codigoestudio = lblCodigoEstudio.Content.ToString(),
codigopaciente = Convert.ToInt32(lblCodigoPaciente.Content.ToString()),
codigoproduccion = Convert.ToInt32(lblNroInforme.Content.ToString()),
codigopromotor = m.codigopromotor,
fecha = Convert.ToDateTime(DateTime.Today.ToShortDateString()),
numeroinforme = Convert.ToInt32(lblNroInforme.Content.ToString()),
revisado = false,
codigozona = (c.codigozona.Value == null ? Convert.ToInt32(c.codigozona) : 0),
codigoclinica = Convert.ToInt32(c.codigoclinica),
codigoclase = e.codigoclase,
}
).FirstOrDefault();
While executing the above code, I'm getting the following error that the stack trace is included:
System.NotSupportedException was caught
Message="The explicit construction of the entity type 'Demo.View.Data.PRODUCCION' in a query is not allowed."
Source="System.Data.Linq"
StackTrace:
en System.Data.Linq.SqlClient.QueryConverter.VisitMemberInit(MemberInitExpression init)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitSelect(Expression sequence, LambdaExpression selector)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitFirst(Expression sequence, LambdaExpression lambda, Boolean isFirst)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node)
en System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
en System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
en System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
en System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
en Demo.View.InformeMedico.realizarProduccionInforme(Int32 codigoExamenxAtencion, Double precioEstudio, Int32 comi) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 602
en Demo.View.InformeMedico.UpdateEstadoEstudio(Int32 codigo, Char state) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 591
en Demo.View.InformeMedico.btnGuardar_Click(Object sender, RoutedEventArgs e) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 683
InnerException:
Is that now allowed in LINQ2SQL?
Entities can be created outside of queries and inserted into the data store using a DataContext. You can then retrieve them using queries. However, you can't create entities as part of a query.
I am finding this limitation to be very annoying, and going against the common trend of not using SELECT * in queries.
Still with c# anonymous types there is a workaround, by fetching the objects into an anonymous type, and then copy it over into the correct type.
For example:
var q = from emp in employees where emp.ID !=0
select new {Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.ToList();
List<User> users = new List<User>(r.Select(new User
{
Name = r.Name,
EmployeeId = r.EmployeeId
}));
And in the case when we deal with a single value (as in the situation described in the question) it is even easier, and we just need to copy directly the values:
var q = from emp in employees where emp.ID !=0
select new { Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.FirstOrDefault();
User user = new User { Name = r.Name, EmployeeId = r.ID };
If the name of the properties match the database columns we can do it even simpler in the query, by doing select
var q = from emp in employees where emp.ID !=0
select new { emp.First, emp.Last, emp.ID }
One might go ahead and write a lambda expression that can copy automatically based on the property name, without needing to specify the values explictly.
Here's another workaround:
Make a class that derives from your LINQ to SQL class. I'm assuming that the L2S class that you want to return is Order:
internal class OrderView : Order { }
Now write the query this way:
var query = from o in db.Order
select new OrderView // instead of Order
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
// etc.
};
Cast the result back into Order, like this:
return query.Cast<Order>().ToList(); // or .FirstOrDefault()
(or use something more sensible, like BLToolkit / LINQ to DB)
Note: I haven't tested to see if tracking works or not; it works to retrieve data, which is what I needed.
I have found that if you do a .ToList() on the query before trying to contruct new objects it works
I just ran into the same issue.
I found a very easy solution.
var a = att as Attachment;
Func<Culture, AttachmentCulture> make =
c => new AttachmentCulture { Culture = c };
var culs = from c in dc.Cultures
let ac = c.AttachmentCultures.SingleOrDefault(
x => x.Attachment == a)
select ac == null ? make(c) : ac;
return culs;
I construct an anonymous type, use IEnumerable (which preserves deferred execution), and then re-consruct the datacontext object. Both Employee and Manager are datacontext objects:
var q = dc.Employees.Where(p => p.IsManager == 1)
.Select(p => new { Id = p.Id, Name = p.Name })
.AsEnumerable()
.Select(item => new Manager() { Id = item.Id, Name = item.Name });
Within the book "70-515 Web Applications Development with Microsoft .NET Framework 4 - Self paced training kit", page 638 has the following example to output results to a strongly typed object:
IEnumerable<User> users = from emp in employees where emp.ID !=0
select new User
{
Name = emp.First + " " + emp.Last,
EmployeeId = emp.ID
}
Mark Pecks advice appears to contradict this book - however, for me this example still displays the above error as well, leaving me somewhat confused. Is this linked to version differences? Any suggestions welcome.
I found another workaround for the problem that even lets you retain your result as IQueryale, so it doesn't actually execute the query until you want it to be executed (like it would with the ToList() method).
So linq doesn't allow you to create an entity as a part of query? You can shift that task to the database itself and create a function that will grab the data you want. After you import the function to your data context, you just need to set the result type to the one you want.
I found out about this when I had to write a piece of code that would produce a IQueryable<T> in which the items don't actually exist in the table containing T.
pbz posted a work around by creating a View class inherited from an entity class that you could be working with. I'm working with a dbml model of a table that has > 200 columns. When I try and return the whole table I get "Root Element missing" errors. I couldn't find anyone who wanted to deal with my particular issue so I was looking at rewriting my entire approach. Just creating a view class for the entitiy class worked in my case.
As pbz suggests : Create a view class that inherits from your entity class. For me this is tbCamp so :
internal class tbCampView : tbCamp
{
}
Then use the view class in your query :
using (var dc = ConnectionClass.Connect(Dev))
{
var camps = dc.tbCamps.Select(s => new tbCampView
{
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
SmartTableViewer(camps, dg1);
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
Here is what happens when you try and create an entity class object in the query.
I didn't want to have to use an anonymous type if I could help it because I wanted the type to be tbCamp. Since tbCampView is of type tbCamp the is operator works well. see Brian Hasden's answer Passing a generic List<> in C#
I'm surprised this is even an issue but with larger tables I run into this error so I thought I would just show it here :
When trying to read this table into memory I get the following error. There are < 2000 rows but the columns are > 200 for each. I don't know if that is an issue or not.
If I just want a few columns I need to create a custom class and handle that which isn't that big of a pain. With the approach pbz provided I don't have to worry about it.
Here is the entire project in case it helps someone.
public partial class Form1 : Form
{
private const bool Dev = true;
public Form1()
{
InitializeComponent();
}
private void btnGetAllCamps_Click(object sender, EventArgs e)
{
using (var dc = ConnectionClass.Connect(Dev))
{
IQueryable<tbCampView> camps = dc.tbCamps.Select(s => new tbCampView
{
// Project columns as needed.
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
// pass in as a
SmartTableViewer(camps);
}
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
//var newRecord = record;
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
internal class tbCampView : tbCamp
{
}
}

Resources