Getting timestamps in deterministic way in Hyperledger Composer transactions - hyperledger

Is there a deterministic way to get timestamp in transaction function, similar to stub.GetTxTimestamp() that can be used in Go version of Fabric's chaincode.

Just sharing an example that works with basic-sample-network network:
In the model file (lib/org.acme.sample.cto) I extended SampleAsset definition any added new property called timestamp of type DateTime:
asset SampleAsset identified by assetId {
o String assetId
--> SampleParticipant owner
o String value
o DateTime timestamp
}
In the script file (lib/logic.js), the onSampleTransaction function to update SampleAsset's timestamp with current transaction's timestamp:
function onSampleTransaction(sampleTransaction) {
sampleTransaction.asset.value = sampleTransaction.newValue;
sampleTransaction.asset.timestamp = sampleTransaction.timestamp;
return getAssetRegistry('org.acme.sample.SampleAsset')
.then(function (assetRegistry) {
return assetRegistry.update(sampleTransaction.asset);
});
}

All transactions have a system property called timestamp, so you can use myTransaction.timestamp.

we cannot use the proto from the vendor folder ...
https://github.com/hyperledger-archives/fabric/issues/1832

Related

failure in serializing optional date type filed to avro regardless of null value or non-null value

We are using avro1.8.2 to serialize data with optional date type field to be published to topic.
record aRecord {
/** Variable: lastUpdate
* lastUpdate indicates the latest date and time the reference asset was updated
*/
union {null, date} lastUpdate = null;
/** Variable: businessDate
* businessDate indicates the business date of the reference asset price
*/
union {null, date} businessDate = null;
}
Ran into the following exception while using the avro tool generated java class to serialize the data:
Error serializing avro message
Caused by: org.apache.avro.AvroRunTimeException: Unknown datum type org.joda.time.LocalDate: 2021-09-17
at org.apache.avro.generic.GenericData.getSchemaName(GenericData.java:772)
at org.apache.avro.specific.SpecificData.getSchemaName(SpecificData.java:302)
at org.apache.avro.generic.GenericData.resolveUnion
Please note that2 this happens regardless of the value is null or non-null (as shown value 2021-09-17 also caused the exception)
We did the following investigation and experiment but could not figure it out why:
Making the date field mandatory, the issue is resolved.
This is because DATE_CONVERSION is added to the corresponding field in the java class generated by avro tool.
If this field is defined as optional and default value is null, DATA_CONVERSION is not added to the java file generated by avro tool.
Using avro 1.9.1 resolved the issue unfortunately we must use avro 1.8.2
We also tried a few other versions of kafka-avro-serializer and spring-boot kafka framework. Nothing works for us.
Other projects that depend on avro1.8.2 seems to be able to handle this and we checked all the places as far as we considered relevant
and all the codes are the same except that somehow they have DATE_CONVERSION in place in the java file
generated by avro tool (although they are defined in advl file exactly the same).
Debuggin into the GenericData.java we found that if DATE_CONVERSION is in place for optional date field, getSchemaName is not called at all.
The getSchemaName basically checks of the type of the object, whether it's an Int, Record, String,...etc.
The date is a logicaltype of joda. Its real type is int as far as we understand
So our questions are:
How to make avro tool enable DATE_CONVERSION for optional "date" type field using avro 1.8.2?
If DATE_CONVERSION is not the key to resolve the issue, what's the best practice to serialize date type field using avro 1.8.2?
and this field could be null (default) or non-null.
Thanks.
SpecificData specificData = SpecificData.get();
specificData.addLogicalTypeConversion(new DateConversion());
DatumWriter<MessageClass> dw = new SpecificDatumWriter<MessageClass>(message.getSchema(), specificData);
DataFileWriter<MessageClass> dfw = new DataFileWriter<MessageClass>(dw);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
dfw.create(message.getSchema(), outputStream);
dfw.append(message);
dfw.close();
ProducerRecord<String, byte[]> record = new ProducerRecord<>(topic, key, outputStream.toByteArray());
return kafkaProducer.send(record, new Callback());
The above code fixed the issue. MessageClass is the java code generated by avro tool.
message is wrapped in specificData which is constructed with new DateConversion()
DATE_CONVERSION is exactly what is needed for optional date field during serialization.
Note that this solution is only needed as a workaround to avro1.8.

ToMany relation gives type error at link()

I have the table:
#Entity()
#JsonSerializable()
class DateTimeStruct {
#JsonKey(ignore: true)
int id = 0;
DateTime time = DateTime.now();
String? title;
DateTimeStruct();
factory DateTimeStruct.fromJson(Map<String, dynamic> json) => _$DateTimeStructFromJson(json);
Map<String, dynamic> toJson() => _$DateTimeStructToJson(this);
}
and
#JsonSerializable(explicitToJson: true)
#Entity()
class Event {
final Time = ToMany<DateTimeStruct>();
}
When I try to query related table with link, like this:
Store.box<Event>().query().link(Event_.Time,
DateTimeStruct_.time.greaterOrEqual(DateTime.now().millisecondsSinceEpoch));
I get the
The argument type 'QueryRelationMany<Event, DateTimeStruct>' can't be assigned to the parameter type 'QueryRelationProperty<Event, DateTimeStruct>'
error message.
Second problem: DateTimeStruct_.time resolved as QueryIntegerProperty<DateTimeStruct> time, but it is a DateTime. Has it converted to integer in database?
Given entities won't work with pub run build_runner build because Event is missing an id
You're using ToMany relations - you'd only want to do that if you want to store multiple DateTimeStruct for each Event - just making sure you're aware. If it's fine to only have a single time per event (which, conceptually, sounds right to me), use ToOne.
With ToMany, you need to link using .linkMany() instead of .link() - that's where the compilation error comes from.
Yes Dart DateTime objects are stored as a millisecond timestamp (unless you specify otherwise using #Property(type: PropertyType.dateNano) annotation). You need to query them as such - you seem to be doing that correctly.

Unable to bind parameter in Insight.Database

I am trying to bind a parameter to a SQL query in my repository but having an error
public IList<Movie> FindMovieById(int movieId)
{
return Database.Connection().QuerySql<Movie>("select * from myDB.movies where ID=?", new { movieId });
}
I get an OleDb exception.
SQL0313: Number of host variables not valid.
Cause . . . . . : The number of host variables or entries in an SQLDA or descriptor area specified in either an EXECUTE or OPEN statement is not the same as the number of parameter markers specified in the prepared SQL statement S000001. If the statement name is *N, the number of host variables or entries in a SQLDA or descriptor area was specified in an OPEN statement and is not the same as the number of host variables specified in the DECLARE CURSOR statement for cursor C000001. Recovery . . . : Change the number of host variables specified in the USING clause or the number of entries in the SQLDA or descriptor area to equal the number of parameter markers in the prepared SQL statement or the number of host variables in the DECLARE CURSOR statement. Precompile the program again.
I have used ? for parameter binding as OleDb has positional parameters which are denoted by '?' rather the '#parameterName'.
Any help is appreciated.
Using Insight.Database can you try this?
return Database.Connection().QuerySql<Movie>(
"select * from myDB.movies where ID=#movieId",
new { movieId = movieId });
In order for Insight to map queries to objects, it binds parameters by name, not by position.
Your parameter object can be an anonymous type. If you specify:
new { movieId }
I believe the compiler will generate a property with the name "MovieID"
In that case, your parameter should be called #movieId.
If your database doesn't support named parameters, please open an issue up on github and I can take a look at it.
https://github.com/jonwagner/Insight.Database/issues

Breeze Entity defaultResourceName is undefined for sub-types when using inheritance

I have a Breeze Web API with a Vehicles, Cars, and Buses. Car and Bus types inherit from Vehicle type in a table per hierarchy database structure.
According to the Breeze docs one should be able to make the same query to call either the local cache (with executeQueryLocally) or the remote service (with executeQuery).
This does not work in the inheritance scenario for Buses and Cars because these types have their defaultResourceName = undefined. But it is odd that a call to the remote service works but not to the local cache. Code explains better:
var EntityQuery = breeze.EntityQuery;
var manager = new breeze.EntityManager('../../breeze/breeze');
var getRemoteCars = function() {
var query = EntityQuery.from('Cars');
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) { console.log('Retrieved Cars from remote data source'); }
function queryFailed(data) { console.log('Failed to retrieve Cars from remote data source'); }
var getLocalCars = function () {
console.log("getLocals called");
var newQuery = new EntityQuery('Cars');
var cars = manager.executeQueryLocally(newQuery);
if (cars) console.log("retrieved some cars from local cache");
else console.log("no cars retrieved from local cache");
};
getRemoteCars().then(getLocalCars);
This code outputs:
Retrieved Cars from remote data source WebApiTest.html:26
getLocals called
Q] Unhandled rejection reasons (should be empty): []
The relevant error message is hidden by a Q.js error (which is a nuisance). Breeze.js threw an error that didn't make it to the Browser:
Cannot find an entityType for either entityTypeName: 'undefined' or resourceName: 'Cars'
It turns out the sub-types (Car and Bus) have defaultResourceName = undefined.
So I can fix the problem by adding:
manager.metadataStore.setEntityTypeForResourceName("Cars", "Car");
manager.metadataStore.setEntityTypeForResourceName("Buses", "Bus");
But that doesn't explain why the remote call worked.
So, firstly is this a bug that will be fixed and secondly, why does the remote call work when the local one does not?
EDIT 24 May 15:15 - More interesting behavior...
The above call to setEntityTypeForResourceName() must occur after the metadata has been retrieved from the server. But if you want to configure the metadataStore before this, you can use the fully qualified name like this:
manager.metadataStore.setEntityTypeForResourceName("Cars", "Car:#VerySimpleVehicleModel.Models");
manager.metadataStore.setEntityTypeForResourceName("Buses", "Bus:#VerySimpleVehicleModel.Models");
Interestingly, with this solution the defaultResourceName for Car and Bus types remain undefined, but the local call works. Strange, no??
We were able to reproduce the problem and it does appear to be a bug.
We are working on the fix.

While Adding New Entity, Identity value is not generated

I am running into problem, where i extend the Entity to expose hasValidationError. Without that it works fine. Also i found that if I supply the ID before adding the entity it works fine as well. Why is the ID field not auto generating once the entity is extended on the client.
I am now using little different version of the code(i find it more intuitive to extend the entity this way), but it still errors out in the same way.
var Country = function () {
console.log("Country initialized");
var self = this;
self.Country_ID = ko.observable("");
self.Country_Code = ko.observable("");
self.Country_Name = ko.observable().extend({
validation: {
validator: function (val, someOtherVal) {
return false;//val === someOtherVal;
},
message: 'Invalid Value!',
params: 5
}
});
var prop = ko.observable(false);
var onChange = function () {
var hasError = self.entityAspect.getValidationErrors().length > 0;
if (prop() === hasError) {
// collection changed even though entity net error state is unchanged
prop.valueHasMutated(); // force notification
} else {
prop(hasError); // change the value and notify
}
};
// observable property is wired up; now add it to the entity
self.hasValidationErrors = prop;
//dummy property to wireup event
//should not be used for any other purpose
self.hasError = ko.computed(
{
read: function () {
self.entityAspect // ... and when errors collection changes
.validationErrorsChanged.subscribe(onChange);
},
// required because entityAspect property will not be available till Query
// return some data
deferEvaluation: true
});
self.fullName = ko.computed(
function () {
return self.Country_Code() + " --- " + self.Country_Name();
});
};
store.registerEntityTypeCtor("Country", Country);
and then in the button click i am using the following code to create new entity.
var countryType = manager.metadataStore.getEntityType("Country");
var newCountry = countryType.createEntity();
//newCountry.Country_ID(200); //if i add this line no errors occurs
newCountry.Country_Code("India");
self.list.push(newCountry);
manager.addEntity(newCountry); // validation error occurs right after this line
self.selectedItem(newCountry);
self.list.valueHasMutated();
Entities will only get their own autogenerated key if the metadata for their type specifies that this is supported. i.e.
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.Identity)
This setting means that the key property of the entity is automatically generated by the server, typically for an 'Identity' column on your database.
or
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.KeyGenerated)
This setting means that you have a server side KeyGenerator that can generate the key for you.
By default, however, myEntityType.autoGeneratedKeyType will equal AutoGeneratedKeyType.None.
In either of the other two cases, breeze will generate a temporary key on the client and then fix it up after a save completes with a 'real' key generated on the server.
If you do not need this capability, then simply create your own ctor for your type that generates a unique key and set it there. see MetadataStore.registerEntityTypeCtor for more details on how to register your ctor.
We are planning on improving our documentation in this area but haven't yet gotten there. I hope this helps.
Perhaps there is nothing wrong at all. How do you know that the id generation is failing? Is it a negative number after adding the newCountry to the manager? It should be.
What is the validation error that you are getting? Does it relate to Country_ID? Perhaps you have a validation constraint (e.g., minimum value) on Country_ID?
The addhasValidationErrorsProperty entity initializer works as intended. I just added a teaching test to the DocCode sample (see "Can create employee after registering addhasValidationErrorsProperty initializer" in entityExtensionTests.js). We haven't deployed it as I write this but you can get it from GitHub.
It follows your example as best I can with the Northwind Employee entity which has an identity id (Employee_ID). The test shows adding the initializer that I wrote in the previous post (not as you may have rewritten it). It shows that the new Employee's id is zero before adding to the manager and becomes -1 after adding to the manager. The -1 is the temporary id of the new employee; it receives a permanent value after save.
The default validationOptions of an EntityManager are set so to validate an entity when it is attached (or added) to the manager. You can change that behavior to suit your needs.
The new employee is in an invalid state when created; it lacks the required First and Last name values (the test displays these errors). Therefore the hasValidationErrors observable becomes true after adding the new employee to the manager and the hasValidationErrors observable raises a change notification that a Knockout UI would hear; these test shows these two points.

Resources