I have made a query in Hyperledger Composer where in the query tries to search for all invoices of a borrower. The borrower is a participant of the invoice asset:
asset Invoice identified by invoiceId {
o String invoiceId
o String invoiceRef optional
o DateTime dateCreated
o String type
o DateTime invoiceOrPurchaseDate optional
o Double amount
o DateTime invoiceDueDate optional
o String paymentStatus optional
o Boolean overdue
o Double outstandingBalance
--> Participant borrower
--> Participant lender
}
I need a query that will return all invoices of a borrower and I did this in Hyperledger composer by coding below:
query QInvoiceByBorrower {
description: "Select invoice by borrower"
statement:
SELECT org.n.blockchaindemo.Invoice
WHERE (_$borrower == borrower)
}
But when I try to call the query through REST API, I get [] empty result as below:
http://10.10.4.244:3000/api/queries/QInvoiceByBorrower?borrower=resource:org.n.blockchaindemo.User#1381991
May I know how to create a query that will search using a foreign relation in Hyperledger Composer?
In you Invoice definition you need to refer to your specific Participant type i.e. User rather that the system type Participant. So the last part of your Invoice will be:
o Double outstandingBalance
--> User borrower
--> User lender
}
With this change your Query should work. A useful diagnostic method is to create a duplicate query without the where clause and then examine the returned data to help understand the parameters to use for a query.
(By using Participant I think your asset will include the first participant type in your model at the time the asset type was first used - this sounds like it could generate some unexpected behaviours, so specifying the actual participant type makes sense.)
Related
I am trying to create a chain which holds a document.
I have created the asset in the model file as
asset document identified by documentid {
o String documentid
o String formImageBase64 //will use IPFS Later
o String nameOfSignator
o String addressOfSignator
o Integer ageOfSignator
o Boolean isSigned
o DateTime dateTimeOfSigning
}
And there are multiple documents containing information of different people.
If the document is updated(new asset is created), How to show a link between the old document and the updated document.
Update
Changing Title from "Creating Application Form using Hyperledger Composer"
To "Chaining/Connecting updated assets in Hyperledger Composer"
I don't know what type of relation you want to show between the past documents and the new documents but if you want to show the historical information of the transactions then you can use HistorianRecord
On the New Updating document, I added a new string which contains the unique "document of the previous string. Making it kind of a singly linked list.
asset document identified by documentid {
o String documentID
o String previousDocumentID // optional(later) original document "0" for now
o String formImageBase64 //will use IPFS Later
o String nameOfSignator
o String addressOfSignator
o Integer ageOfSignator
o Boolean isSigned
o DateTime dateTimeOfSigning
}
This way I can chain documents together.
I am developing a blockchain application using Hyperledger Composer.
This is a part of my model i.e. .cto file:
concept Address{
o String addressId
o String PersonInChargeFullName
o String title
o String address1
o String address2 optional
o String city
o String country
o String zipcode regex=/^\d{5}$/
o String phone regex=/^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$/
o String email regex=/^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/
o Boolean isActive default=true
}
abstract participant Company identified by companyId{
o String companyId
o String companyName
o Address defaultAddress
}
participant OEM extends Company {
o Address[] plants
}
From this page, I understand that if we use optional as a field validator, which means that that field is optional, and others are not. Am I right?
Although, I have not made PersonInChargeFullName as optional, when I submit the data (for eg: "PersonInChargeFullName": "",) from composer-rest-server API, the transaction gets processed, without any error.
Please advise.
Here option is little bit of different concept try running your code without PersonInChargeFullName field not as PersonInChargeFullName = "" but don't include it in json input i am pretty sure you will get error.
I have a query deinged in hyperledger composer queries.qry file.
query checkOwnerUniqueness{
description: "Select owner with given identifier"
statement:
SELECT org.acme.participant.Owner
WHERE (idNum == _$idNum)
}
Now My query works if id number is abc123 and user search for abc123 but it fails if user pass on value as ABC123. However I would like query to respond with owner whether characters are entered in lower case or upper case.
I have already tried general SQL API's i.e. LOWER() and UPPER() but seems they doesn't work in the Hyperleger composer Query language. SO some can please help me understand on how to do this in Hyperledger Composer query file.
I would first suggest that IDs are inserted in the first place (ie programatically) with the same case. Composer simply passes on the query to CouchDB's query language FYI and honours the case sensitivity at which the data was entered.
Are you aware that you can use regex to validate the ID field (see Modeling language docs here ? - seems quite an important field to me (as opposed to say a surname field).
You can use otherwise buildQuery function (eg. with one parameter still) in your function - or if you insist in keeping the QUERY in queries.qry then supply two (lower and upper):
query checkOwnerUniqueness{
description: "Select owner with given identifier"
statement:
SELECT org.acme.participant.Owner
WHERE (idNum == _$upper ID idNum == _$lower )
}
var str = "joe123";
var lower = str.toLowerCase();
var upper = str.toUpperCase();
return query('checkOwnerUniqueness', {idNum: str} ) // ID passed in lower case
// OR
// return query('checkOwnerUniqueness', {idNum: lower, idNum: upper}) // upper or lower two parms
.then(function (results) {
for (var n = 0; n < results.length; n++) { // blah
// process all objects returned in the query
}
});
Giving you alternatives anyway - I think I would make sure the ID field is always entered in the same case, just saying.
Sorry in advance if this question is unclear. Please tell me what to change to make it a better question.
I am currently maintaining a C# WinForm system where I'm trying to learn and use DDD and CQRS principles. Vaughn Vernon's Implementing Domain Driven Design is my main DDD reference literature.
The system currently uses legacy code which makes use of Data Aware Controls.
In the Asset Inventory Context, i have designed my aggregate root Asset which composes of multiple valueObjects which are standard entries in the system:
In this Context, i'm trying to implement a use case where the user can manually register an Asset to the system.
My current implementation is the following:
From Presentation Layer:
Upon loading the RegisterAssetForm.cs it loads the existing standard entry lists of Group, ItemName, etc. through the Data Aware controls, all consisting of data rows with columnsid: int and name: string.
When the user selects the desired ItemName, Group, PropertyLevel, Department, and Category, then clicks save, a command is performed:
RegisterAssetForm.cs
...
AssetInventoryApplicationService _assetInventoryServ;
...
void btnSave_Click(object sender, EventArgs e)
{
int itemNameId = srcItemName.Value // srcItemName is a custom control whose Value = datarow["id"]
int groupId = srcGroup.Value;
string categoryId = srcCategory.Value;
string departmentId = srcDepartment.Value;
string propLvlId = srcPropLevel.Value;
...
RegisterAssetCommand cmd = new RegisterAssetCommand(itemNameId, groupId, categoryId, departmentId, propLvlId);
_assetInventoryServ.RegisterAsset(cmd);
...
}
From Application Layer:
The AssetInventoryApplicationService depends on domain services.
AssetInventoryApplicationService.cs
...
IAssetRepository _assetRepo;
...
public void RegisterAsset(RegisterAssetCommand cmd)
{
...
AssetFactory factory = new AssetFactory();
AssetID newId = _assetRepo.NextId();
Asset asset = factory.CreateAsset(newId, cmd.ItemNameId, cmd.PropertyLevelId,
cmd.GroupId, cmd.CategoryId, cmd.DepartmentId);
_assetRepo.Save(asset);
...
}
From Domain Layer:
AssetFactory.cs //not my final implementation
...
public class AssetFactory
{
...
public Asset CreateAsset(AssetID id, int itemNameId, int propLvlId, int groupId, int categoryId, int departmentId)
{
ItemName itemName = new ItemName(itemNameId);
PropertyLevel propLvl = new PropertyLevel(propLvlNameId);
Group group = new Group(groupNameId);
Category category = new Category(categoryNameId);
Department department = new Department(departmentNameId);
return new Asset(id, itemName, propLvl, group, category, deparment);
}
...
}
Sample table of what fills my value objects
+------------+--------------+
| CategoryID | CategoryName |
+------------+--------------+
| 1 | Category1 |
| 2 | Category2 |
| 3 | Category3 |
| 4 | Category4 |
| 5 | Category5 |
+------------+--------------+
I know domain models must be persistence-ignorant that's why i intend to use surrogate identites (id field) in Layer Supertype with my valueobject to separate the persistence concern from the domain.
The main property to distinguish my value objects is their Name
From the presentation layer, i send the standard entry value as integer id corresponding to primary keys through a command to the application layer which uses domain services.
Problem
* Is it right for me to pass the standard entry's id when creating the command, or should I pass the string name?
* If id is passed, how do i construct the standard entry value object if name is needed?
* If name is passed, do i need to figure out the id from a repository?
* Or am I simply designing my standard entry value objects incorrectly?
Thanks for your help.
It looks to me that you may be confusing a Value Object and an Entity.
The essential difference is that an Entity needs an Id but a VO is a thing (rather than a specific thing). A telephone number in a CRM would likely be a VO. But it would likely be an Entity in if you are a telephone company.
I have an example of VO in this post which you may find helpful - you can get it here
To answer your 'Problems' more specifically:
If you are creating some entity then it can be advantageous to pass in the id to a command. That way you already know what the id will be.
You shouldn't be able to create an invalid value object.
Why can't you pass in the name and the ID? Again - not sure this is relevant to a Value Object
I think you have designed them incorrectly. But I can't be sure because I don't know your specific domain.
Hope this helps!
Instead of giving full update access to a participant I'd like to give him update access to a specific property within the asset.
From what I've read from the documentation it's possible to apply a rule to a property.
Rule:
rule PatientAddMentor {
description: "A patient is allowed to add mentors"
participant(p): "nl.epd.blockchain.Patient"
operation: UPDATE
resource(m): "nl.epd.blockchain.MedicalFile.mentors"
condition: (m.owner.getIdentifier() == p.getIdentifier())
action: ALLOW
}
The part where I am stuck at is how to target the asset in the condition. I'm assuming (m) equals the mentors in the condition because the error message says that the participant does not have UPDATE rights. (I'm not sure if it's even possible.)
Model:
namespace nl.epd.blockchain
asset MedicalFile identified by bsn {
o String bsn
--> Patient owner
--> Patient[] mentors optional
--> Organisation[] organisations optional
o Visit[] visits optional
o String[] allergies optional
o Treatment[] treatments optional
o Medicine[] medicine optional
}
participant Patient identified by bsn {
o String bsn
o String firstName
o String namePrefix optional
o String lastName
o String email
o String telephoneNumber
o String birthday
o String gender
o String city
o String zipCode
o String street
o String houseNumber
o String houseNumberExtra optional
}
The ACL engine does not yet enforce property level access control (only namespace and class level access control has been implemented).
You will have to use programmatic access control using the getCurrentParticipant() runtime API until declarative access control for properties is implemented.