Can i make changes to the parameters defined in relationships - hyperledger-fabric

1.i want to know whether i can make changes to the parmeter defined in the relationship.
what i want to do is to make a function with name of bookflight and then make changes to the number of seats if the flight is booked .
these are my cto files
namespace org.acme.airline.aircraft
/** Aircraft is an ACME Asset*/
asset Aircraft identified by aircraftId {
o String aircraftId
o Ownership ownershipType default="LEASED"
// Number of seats per class
o Integer firstClassSeats range = [4,]
o Integer businessClassSeats range = [6, 20]
o Integer economyClassSeats range = [30, ]
o String nickName optional
}
enum Ownership {
o LEASED
o OWNED
}
and for flight the code is
namespace org.acme.airline.flight
import org.acme.airline.aircraft.Aircraft
asset Flight identified by flightId {
// Solution to the exercise - try out the Regular expression at http://regex101.com
// Share your optimized regular expression with others :)
o String flightId 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 flightNumber
o Route route
o String[] aliasFlightNumber optional
--> Aircraft aircraft optional
}
concept Route {
o String origin regex=/[A-Z][A-Z][A-Z]/
o String destination regex=/[A-Z][A-Z][A-Z]/
o DateTime schedule
}
// Logistics department of ACME creates the flights
transaction CreateFlight {
o String flightNumber
o String origin
o String destination
o DateTime schedule
}
event FlightCreated {
o String flightId
}
// Assigns an aircraft to the flight
// The logistics / validation on availability of aircraft
// Kept outside of the Blockchain
transaction AssignAircraft {
o String flightId
o String aircraftId
}
// Event indicating that aircraft was assigned
event AircraftAssigned {
o String flightId
o String aircraftId
}
now what i want to make changes to reltionship in the flight
to make changes to it what should i do . i have made a javascript file.to
access make changes in it.
function booktickets(registry){
//array to recored the hold the instances of aircraft resourse
const bnDef = bnUtil.connection.getBusinessNetwork();
const factory = bnDef.getFactory();
let flightResource = factory.newResource(aircraftNamespace,aircraftType,'AE201-05-05-2020');
flightResource.setPropertyValue('flightNumber','AE101');
flightResource.route = factory.newConcept(aircraftNamespace,'Route');
flightResource.route.setPropertyValue('origin', 'DEL');
flightResource.route.setPropertyValue('destination' , 'APH');
flightResource.route.setPropertyValue('schedule' , new Date('2018-10-15T21:44Z'));
flightResource.aircraft = factory.newRelationship('org.acme.airline.aircraft', 'Aircraft', 'CRAFT01');
//.setPropertyValue()
flightResource.aircraft.setPropertyValue('ownershipType','LEASED');
flightResource.aircraft.setPropertyValue('firstClassSeats',10);
flightResource.aircraft.setPropertyValue('businessClassSeats',10);
flightResource.aircraft.setPropertyValue('economyClassSeats',100);
return registry.update(flightResource).then(()=>{
console.log('Successfully created the flight!!!');
bnUtil.disconnect();
}).catch((error)=>{
console.log(error);
bnUtil.disconnect();
});
}

your question appears to be: can you create a relationship from Flight (asset) to Aircraft (asset) in a transaction function (that operates in the chaincode runtime), and can you update the fields of the related Aircraft (in its separate registry). The answer is 'yes', you can, for both. You don't provide the model for the bookflight function, so can only make assumptions about its model definition. as a minimum (based on your code) it will need:
transaction bookflight { }
A code example of what you are trying to do with relationships - is shown here -> https://github.com/hyperledger/composer-sample-networks/blob/master/packages/perishable-network/lib/logic.js#L130
This section:
const bnDef = bnUtil.connection.getBusinessNetwork();
const factory = bnDef.getFactory();
is composer-client code - and will not work inside a transaction function (that is runtime code, you need to remove client code - the example below shows 'how' to do it.) Replace line 2 with :
const factory = getFactory();
See more on transaction functions, examples etc at https://hyperledger.github.io/composer/latest/reference/js_scripts
note: you can just assign values like:
flightResource.route.origin = 'DEL' ; // no need for.setPropertyValue('origin', 'DEL'); etc etc
I don't see your code to update the Aircraft registry (with flightResource.aircraft FYI) - but you would need that to update the fields in that related asset (presently, you're only updating the Flight registry above )
new Date() is non deterministic code - if you're hoping to achieve consensus from multiple peers/orgs.
You'll notice the link I sent earlier, shows the use of async/await rather than JS promises (.then etc etc) - easier to code, easier to read. cheers.

Related

How can I manage my assets in hyperledger composer?

In my hyperledger composer project I have a medicine as an asset. There are different types of medicine, but all medicine need to be approved before they are allowed to be produced and distributed in the supply chain.
Can I store a list of allowed assets in the blockchain, a list which can grow or shrink? Or do I have to store it off-chain?
edit: grammar mistake fixed.
Based on your reply to Riccardo Bonesi, I suggest something like this
asset AllowedMedicines identified by id {
o String id
o Medicines[] medicines
}
concept Medicines {
o String medicineId
o String medicineName
o Participants[] allowedParticipants
}
concept Participants {
o String participantId // either this is one below
--> Participant somePerson
// Any specific meta data you want to store
}
Now in your .js files you can do something like this
const allowedMedicines = await registry.get(id);
const participant; // The person you are checking for
const medicineId; // The medicine against which you are checking
const medicines = allowedMedicines.medicines;
if (medicines.medicineId.contains(medicineId)) {
// Medicine is in the list;
let allowedParticipants = medicines.allowedParticipants;
if (allowedParticipants.contains(participant) {
// The participant is allowed access to the medicine
};
};
Now of course based on the composer version, some syntax may need to be tweaked, but is the general idea of how you can maintain a mapping.

hyperledger composer adding Participants to array of Participants in transaction process

I have defined Participant and asset as below and I want to add a particular participant to array of access[type Participant] in transaction. Can someone tell me how this can be done.
How can I add the participant identity to the array. How do I need to pass the identity through params and then add it to the array of participants. I have created the permissions according to the question I asked earlier. Link to the question for permission
ASSET DECLARATION
asset Details identified by detailsId {
o String detailsId
o String description optional
--> Bank owner
o Bank[] access optional
o String document
}
PARTCIPANT DECLARATION
participant Bank identified by bankId {
o String bankId
o String Name
}
You have made a small change to the model between the ACL example and this example - changing the access array which was a relationship before.
--> Bank[] access optional original
vs
o Bank[] access optional here
Based on the original model, first add this Transaction to the model:
transaction AddAccess {
--> Details details
--> Bank additionalAccess
}
Then, this scripted Transaction should work for you:
/**
* Adding a Bank to an access list
* #param {org.example.test.AddAccess} addA
* #transaction
*/
async function addAccess(addA) {
// Add the new access to the array, checking if empty first as it is optional field at creation
if (typeof addA.details.access == 'undefined') {
addA.details.access = new Array();
addA.details.access[0] = addA.additionalAccess;
}
else {
addA.details.access.push(addA.additionalAccess);
}
// get the Registry
const assetRegistry = await getAssetRegistry('org.example.test.Details');
// Update the Registry with the new details
await assetRegistry.update(addA.details);
}
Test with JSON like this:
{
"$class": "org.example.test.AddAccess",
"details": "resource:org.example.test.Details#D11",
"additionalAccess": "resource:org.example.test.Bank#9241"
}

Retrieving properties of relationship through query

I would like to create a query which returns all the Requests (asset) in which the Container's (asset) owner's id is equal to the parameter.
Model file (owner of a container is a Company participant, identified by id):
namespace org.acme.shipping.assets
import org.acme.shipping.participants.*
asset Container identified by number {
o String number
o ContainerType type
o String content
o ContainerStatus status default = "FREE"
--> Company owner
}
enum ContainerType {
o DRY
o REEFER
}
enum ContainerStatus {
o LOCKED
o FREE
}
asset Request identified by id {
o String id
--> Container container
}
Query file
query getRequestsByCompany {
description: "Get requests by company"
statement:
SELECT org.acme.shipping.assets.Request
WHERE (container.owner.id == _$company_id)
}
However, the current query does not seem to work. Is this achievable with a query?
I did a lot of research also to do it using query file, but couldnt find a way, so I think that its not possible at moment.
The alternative way is to use loopback filters:
https://github.com/hyperledger/composer-knowledge-wiki/blob/latest/knowledge.md#information_source--filters-loopback
https://loopback.io/doc/en/lb2/Where-filter.html
Something like:
{"where":{"shipmentId":1000}, "include":"resolve"}
you can go one level in like search by number . I am working on it if get the exact solution .
query getRequestsByCompany {
description: "Get requests by company"
statement:
SELECT org.acme.shipping.assets.Request
WHERE (container == _$container)
}

Includes function in ACL condition

I have an asset called MedicalFile which contains a reference to an organization. The participant HealthCareProfessional also belongs to an organization.
Now I'd like to define an ACL rule which limits the health care professional to only view medical files which MedicalFile's are connected to his organisation.
I came up with the following rule:
rule OrganisationMedicalFilePermission {
description: "An organisation may updates a medical file which they have permission from"
participant(h): "nl.epd.blockchain.HealthCareProfessional"
operation: ALL
resource(m): "nl.epd.blockchain.MedicalFile"
condition: (m.organisations.includes(h.organisation))
action: ALLOW
}
This results in an empty array once I invoke the RESTful API with Loopback. I'm authenticated as a health care professional.
Assets & Participant:
asset Organisation identified by id {
o String id
o String name
o String city
o String zipCode
o String street
o String houseNumber
o String houseNumberExtra optional
o OrganisationType organisationType
}
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 HealthCareProfessional identified by bsn {
o String bsn
o String firstName
o String namePrefix optional
o String lastName
--> Organisation organisation
}
My question is if it's possible to create a condition which validates this problem. If not, what are my options?
It's a good question; there's an updated ACL below that I've tested using the online playground.
This is the updated rule:
rule LimitAccess {
description: "An organisation may updates a medical file which they have permission from"
participant(h): "nl.epd.blockchain.HealthCareProfessional"
operation: ALL
resource(m): "nl.epd.blockchain.MedicalFile"
condition: (
m.organisations.some(function (organisation) {
return organisation.getIdentifier() === h.organisation.getIdentifier();
} )
)
action: ALLOW
}
The some function is the critical piece here to scan the array of relationships. Also note the use of the getIdentifier() function as well rather than trying to access the identifier directly.

Field index for queries not updating when value set programmatically

My module creates a custom content item through the controller:
private ContentItem createContentItem()
{
// Add the field
_contentDefinitionManager.AlterPartDefinition(
"TestType",
cfg => cfg
.WithField(
"NewField",
f => f
.OfType(typeof(BooleanField).Name)
.WithDisplayName("New Field"))
);
// Not sure if this is needed
_contentDefinitionManager.AlterTypeDefinition(
"TestType",
cfg => cfg
.WithPart("TestType")
);
// Create new TestType item
var newItem = _contentManager.New("TestType");
_contentManager.Create(TestItem, VersionOptions.Published);
// Set the added boolean field to true
BooleanField newField = ((dynamic)newItem).TestType.NewField as BooleanField;
newField.Value = true;
// Set title (as date created, for convenience)
var time = DateTime.Now.ToString("MM-dd-yyyy h:mm:ss tt", CultureInfo.InvariantCulture).Replace(':', '.');
newItem.As<TitlePart>().Title = time;
return newItem;
}
The end result of this is a new TestType item with a field that's set to true. Viewing the content item in the dashboard as well as examining ContentItemVersionRecord in the database confirms that the value was set correctly.
However, queries don't seem to work properly on fields that are set in this manner. I found the record IntegerFieldIndexRecord, which is what I assume projections use to fill query result pages. On this, the value of TestField remains at 0 (false), instead of 1 (true).
Going to the content item edit page and simply clicking 'save' updates IntegerFieldIndexRecord correctly, meaning that the value is now picked up by the query. How can the record be updated for field values set programmatically?
Relevant section of migration:
SchemaBuilder.CreateTable(typeof(TestTypePartRecord).Name, table => table
.ContentPartRecord()
);
ContentDefinitionManager.AlterTypeDefinition(
"TestType",
cfg => cfg
.DisplayedAs("Test Type")
.WithPart(typeof(TitlePart).Name)
.WithPart(typeof(ContainablePart).Name)
.WithPart(typeof(CommonPart).Name)
.WithPart(typeof(IdentityPart).Name)
);
Edit: The fix for this is to manually change the projection index record whenever changing a field value, using this call:
_fieldIndexService.Set(testResultItem.As<FieldIndexPart>(),
"TestType", // Resolves as TestTypePart, which holds the field
"newField",
"", // Not sure why value name should be empty, but whatever
true, // The value to be set goes here
typeof(bool));
In some cases a simple contentManager.Publish() won't do.
I've had a similar problem some time ago and actually implemented a simple helper service to tackle this problem; here's an excerpt:
public T GetStringFieldValues<T>(ContentPart contentPart, string fieldName)
{
var fieldIndexPart = contentPart.ContentItem.As<FieldIndexPart>();
var partName = contentPart.PartDefinition.Name;
return this.fieldIndexService.Get<T>(fieldIndexPart, partName, fieldName, string.Empty);
}
private void SetStringFieldValue(ContentPart contentPart, string fieldName, IEnumerable<int> ids)
{
var fieldIndexPart = contentPart.ContentItem.As<FieldIndexPart>();
var partName = contentPart.PartDefinition.Name;
var encodedValues = "{" + string.Join("},{", ids) + "}";
this.fieldIndexService.Set(fieldIndexPart, partName, fieldName, string.Empty, encodedValues, typeof(string));
}
I've actually built this for use with MediaLibrary- and ContentPicker fields (they encode their value as string internally), so it might not be suitable for the boolean field in your example.
But it can't be that hard to implement, just look at the existing drivers and handlers for those fields.
There are 2 ways to fix this:
1) Ensure the newly created item is getting published by calling ContentManager.Publish() as Orchard.Projections.Handlers.FieldIndexPartHandler listens to the publish event to update the FieldIndexPartRecord
2) use IFieldIndexService to update FieldIndexPartRecord manually, see implementation of Orchard.Projections.Handlers.FieldIndexPartHandler to get in idea how to do this
Hope this helps.
:edit
Due to calling Create(...Published) the ContentManager.Published() won't do anything as the item is already considered published.
You can do the following to force the publish logic to run:
bool itemPublished = newItem.VersionRecord.Published;
// unpublish item first when it is already published as ContentManager.Publish() internally first checks for published flag and when set it aborts silently
// -> this behaviour prevents calling publish listeners
if (itemPublished)
_contentManager.Unpublish(newItem);
// the following call will result in calls to IContentHandler.Publishing() / IContentHandler.Published()
_contentManager.Publish(newItem);
or just create the item as a draft and publish it when everything is setup correctly.

Resources