aliceUser <- do
mbAliceUser <- queryContractId alice newAliceCid
case mbAliceUser of
None -> fail "Alice doesn't have a User contract"
Some aliceUser -> return aliceUser
return ()
diff --git a/daml/Main.daml b/daml/Main.daml
index c50e0a9..5dd7a8e 100644
--- a/daml/Main.daml
+++ b/daml/Main.daml
@@ -44,7 +44,7 @@ template DataRequest
do
- create $ DataTransfer with req = requester, acc = acceptor, meddetails = med_det
+ create $ DataTransfer with req = requester, acc = acceptor, meddetails = medet
nonconsuming choice CancelacceptorRequest: ()
controller requester
do
With a little bit more text:
Hi @nikhilreddy,
The Daml compiler is getting confused a bit here. On line 47, you’ve written med_det
, which is not in scope as a local variable. Thus, the compiler looks for it in the global scope, and finds a function med_det : User -> Address
. It then complains that you’re trying to assing a function of type User -> Address
in a field that expects an Address
, and suggests applying the function to an argument (of type User
) in order to get an Address
.
Where does that med_det
function come from? A template declaration like
template User with
username: Party
med_det: Address
age: Int
where
...
implicitly generates a data declaration along the lines of:
data User = User with username : Party, med_det : Address, age: Int
which, in turn, automatically generates functions to extract each field of the record, which you can’t really write in Daml but would look like:
username : User -> Party
username (User p _ _) = p
med_det : User -> Address
med_det (User _ a _) = a
age : User -> Int
age (User _ _ a) = a
You can define your own aliases if you want; something like this would compile fine:
user_age : User -> Int
user_age (User _ _ a) = a
Hi Gary, here I am looking to get the med details directly from USER… to DATA TRANSFER with out Involving DATA REQUEST… I am using DATA REQUEST just for authentication process… when the acceptor accept’s the data request then the reciever get the details
aliceUser <- do
mbAliceUser <- queryContractId alice newAliceCid
case mbAliceUser of
None -> fail "Alice doesn't have a User contract"
Some aliceUser -> return aliceUser
return ()
Your DataTransfer
has a meddetails
field. That means you need to supply a value for that field; there is no way around that. You’ve written when creating DataTransfer
meddetails = med_det
The problem is, what do you intend to say by referring to med_det
here?
If you don’t want to include it directly in the DataRequest
, you could do something else. You’ve assigned username
as a key for User
; you could fetchByKey @User requester
to fetch the appropriate User
contract, grab the med_det
out of it, and include it in the DataTransfer
that way. Since requester
is a signatory to DataRequest
, they effectively agree to having their own contract fetched on invocation of the Accept
choice.
You could also include a contract ID at which the med_det
can be found in the DataRequest
contract, but I think the previous idea is easier to think about.
Yeah, I thought of it but I don’t know how to Implement it
i tried but unable to doit
In order to implement @Stephen’s suggestion, all you need to do is replace lines 46 and 47 with:
(_, user) <- fetchByKey @User acceptor
create $ DataTransfer with req = requester, acc = acceptor, meddetails = med_det user
thank you, It works
can you please help me understand that part
Not anymore, no: it looks like your code has disappeared. Would you mind sharing it again?
Hi I deployed my code in dabl and I am unable to seee med details after creating the contract and I am facing the error like this when I accept the data request
CommandService Error, io.grpc.StatusRuntimeException: INVALID_ARGUMENT: Command interpretation error in LF-DAMLe: dependency error: couldn't find key: GlobalKey(b35991929eb994b8d2f7e956fd4c65203a4914056cecbce7bcbd2cd1f8a7ba59:Main:User, ValueParty(ledger-party-299ee654-4325-4188-9903-9aaf9c426dec)). Details: Last location: [DA.Internal.Template.Functions:216], partial transaction: <empty transaction>.
Here is my code
module User where
import Daml.Script
template User with
username: Party
med_det: Address
age: Int
where
signatory username
key username: Party
maintainer key
nonconsuming choice NewDataRequest: ContractId DataRequest with
acceptor: Party
controller username
do
create $ DataRequest with requester = username, ..
nonconsuming choice AcceptDataRequest: ContractId DataTransfer with
dataRequest: (Party, Party)
controller username
do
exerciseByKey @DataRequest dataRequest Accept
template DataRequest
with
requester: Party
acceptor: Party
where
signatory requester
observer acceptor
key (requester, acceptor): (Party, Party)
maintainer key._1
nonconsuming choice Accept: ContractId DataTransfer
controller acceptor
do
(_, user) <- fetchByKey @User acceptor
create $ DataTransfer with req = requester, acc = acceptor, meddetails = med_det user
nonconsuming choice CancelacceptorRequest: ()
controller requester
do
archive self
template DataTransfer
with
req: Party
acc: Party
meddetails: Address
where
signatory req, acc
key (req, acc): (Party, Party)
maintainer [key._1, key._2]
nonconsuming choice CancelDataTransfer1: ()
controller req
do
archive self
nonconsuming choice CancelDataTransfer2: ()
controller acc
do
archive self
data Address = Address {
history: Text,
cured: Text,
medication: Text
} deriving (Eq, Show)
test = script do
nikhil <- allocateParty "nikhil"
-- jeevan <- allocateParty "jeevan"
chandan <- allocateParty "chandan"
usercid <- submit chandan $ createCmd $ User {username= chandan, med_det = Address {history = "Fever", cured = "NO", medication = "paracetmol"}, age = 21}
usercid <- submit nikhil $ createCmd $ User {username=nikhil, med_det = Address {history = "Throat Infection", cured = "yes", medication = "Azitromycin"}, age = 21}
-- submit jeevan $ createCmd $ User with username=jeevan
submit nikhil $ exerciseByKeyCmd @User nikhil $ NewDataRequest with acceptor=chandan
-- submit chandan $ exerciseByKeyCmd @DataRequest (nikhil,chandan) Accept
Thanks for posting your code again. Let me try to explain in a bit more depth.
The lines:
do
(_, user) <- fetchByKey @User acceptor
create $ DataTransfer with req = requester, acc = acceptor, ```
rely on the fact that your User
template defines its username
field as a key. Defining a key for a template is not mandatory; when a key is defined, it has two effects:
- The
maintainer
is responsible for ensuring that there cannot be two contracts of the same template and with the same key at the same time. - It is possible to fetch the “current contract with this key” without knowing the contract ID in advance. (The contract ID for a given key can change over time if the contract bearing the key is archived and another one with the same key is subsequently created.)
So the effect of those two lines is to attempt to fetch a User
contract with key acceptor
(assuming that’w what your intent was: they accept to share their personal medical details with the requester
), using the authority of the acceptor
as they are the one executing the choice, and then create a DataTransfer containing a copy of the med_det
field of the User
contract we just fetched. (Note that this is a one-time copy, so the requester doe not get future updates.)
fetchByKey
(reference doc, explanations on keys) is an operation that may fail, for example if there is no contract for the given key at the time of execution. The notation (_, user) <- fetchByKey ...
means "execute the fetchByKey
operation over there on the ledger, and then, if you do find a matching contract, discard the contract ID and bind the payload to the local variable user
".
It is part of the semantics of fetchByKey
that it will fail the current transaction if no matching contract is found. You can work around that by using visibleByKey
and/or lookupByKey
, but be aware that they are a bit more restrictive in the permissions they require.
The error message you’re seeing is specifically telling you that: you were not able to find a User
contract for the given Party
. In general, this may be because the contract does not exist, or because the current request does not have sufficient permissions to fetch it. In your case, I strongly suspect the former. In particular, deploying to Daml Hub does not automatically run your test
script, so you may not have the contracts you expect.
I am not sufficiently familiar with Daml Hub myself to give you specific guidance on how to properly initialize your ledger and create the required contracts, but I’m sure someone will chime in with more information on that front.
Also note that unfortunately Daml Hub doesn’t support allocateParty
yet. In your script, you will need to refer to parties that you have already created through the console.
See https://hub.daml.com/docs/quickstart/#run-the-script-against-your-ledger for more information.
thank you somuch
Hi gary can you help me to automatically add the medical contracts of a patient from any database to the ledger
It will depend on these other databases. If you want to have a running process that monitors any change to a live database and propagates those changes to a Daml ledger, the technologies involved will heavily depend on what that other database is and what facilities it offers for detecting changes.
Generally speaking, in that situation, you’ll need to write a program yoourself in whatever general-purpose (i.e. not Daml) language you’re most comfortable with, and that program will interact on the one hand wth your database using its APIs, and on the other hand with the Daml ledger using either the gRPC API or the JSON API.
To facilitate such approaches, we provide Java language bindings and codegen for the gRPC API, as well as JavaScript (actually TypeScript) language bindings and codegen for the JSON API. What the codegen does in both case is automatically translate your Daml types into sensible language-specific data definitions, so you don’t have to do that rather repetitive task yourself.
While we provide tools to make those two languages easier to work with, you should be able to use any language that has gRPC bindings with the gRPC API, and any language that has HTTP support (i.e. “all of them”?) for the JSON API.
If instead you are thinking of a one-time manual import, you can use Daml Script with an input. You would need to use whatever tools your database has to extract your data in the form of a JSON file, and then you would be able to use the --input-file
of the daml script
command to read that file in as Daml data and run a Daml script with that.
Is there any manual to implement the contracts from MONGO DB
Not at this time, no. Your best bet would be some sort of cron job that extracts the data from MongoDB to a JSON file and then runs daml script
with that file as input, I think.
I’d advise some careful design here, though, as replicating data between databases often results in synchronization issues. You’ll have to be very clear on which side is the “master” in such cases.