Search code examples
daml

how can update only Map values in daml contract?


Datatype detail:
  type1: Text
  type2: [detail2]

Datatype detail2:
  type1: Text

template A
with 
    consumerName: Text
    producerName : Text
    details : Map Text detail

create A with consumerName,producerName,details

now need to update contract old Details(Map Text Datatypes) with new Details(Map Text Datatypes) of multiple keyvalues .how can be achieve this using merge without doing multiple contract updation or any other solution is possible ?


Solution

  • You can use the functions in DA.Next.Map to manipulate maps. Here is a complete working example which I hope can shed some light on syntax and simple use-cases:

    module Main where
    
    import Daml.Script
    import qualified DA.Next.Map as Map
    import DA.Next.Map (Map)
    
    template Comments
      with
        producer: Party
        consumer: Party
        productComments: Map Text [Text]
      where
        signatory producer
        observer consumer
        preconsuming choice AddComment: ContractId Comments
          with productName: Text, comment: Text
          controller consumer
          do
            create this with productComments = addComment productComments productName comment
        nonconsuming choice ReadComments: Map Text [Text]
          with reader: Party
          controller reader
          do return productComments
    
    addComment: Map Text [Text] -> Text -> Text -> Map Text [Text]
    addComment prev item newComment = case Map.lookup item prev of
      -- If the key is not in the map yet, we just add the comment
      None -> Map.insert item [newComment] prev
      -- If there are comments already, add to the end
      Some existingComments -> Map.insert item (existingComments ++ [newComment]) prev
    
    setup : Script ()
    setup = script do
      alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
      bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
    
      id1 <- submit bob do
        createCmd Comments
          with producer = bob
               consumer = alice
               productComments = mempty -- start with an empty map
      map1 <- submit alice do exerciseCmd id1 (ReadComments alice)
      assert $ map1 == Map.fromList []
    
      id2 <- submit alice do exerciseCmd id1 (AddComment "item1" "it was not very good")
      map2 <- submit alice do exerciseCmd id2 (ReadComments alice)
      assert $ map2 == Map.fromList [("item1", ["it was not very good"])]
    
      id3 <- submit alice do exerciseCmd id2 (AddComment "item2" "this is great!")
      map3 <- submit alice do exerciseCmd id3 (ReadComments alice)
      assert $ map3 == Map.fromList [("item1", ["it was not very good"]),
                                     ("item2", ["this is great!"])]
    
      id4 <- submit alice do exerciseCmd id3 (AddComment "item2" "I can't stop raving about it")
      map4 <- submit alice do exerciseCmd id4 (ReadComments alice)
      assert $ map4 == Map.fromList [("item1", ["it was not very good"]),
                                     ("item2", ["this is great!", "I can't stop raving about it"])]
    

    Tested on SDK 1.6.0.

    I've also recently answered a possibly related question on the DAML forum, where I have a few more examples of using the functions in DA.Next.Map. Maybe that can help, too.