Converting Daml models to JSON equivalent

Hi!

Are there any existing tools that can take some Daml templates, and for each one, output what that template looks like (with associated types) in its corresponding JSON representation for the HTTP API?

Given the following example, I guess I’m looking for something like

Input:

template Role
  with
    operator : Party
    provider : Party
    observers : Set Party
  where
    signatory operator, provider
    observer observers

    key (operator, provider) :  (Party, Party)
    maintainer key._1

    controller provider can

      nonconsuming OfferRegulatorService : ContractId Regulator.Offer
        with
          customer : Party
        do
          create Regulator.Offer with ..

Output:

// v1/create

{
  payload: {
    "operator": string,
    "provider": string,
    "observers": {
      "map": [
        [
          string,
          {}
        ]
      ]
    }
  },
  templateId: "72390a820b1bbb8dbf035bb070f93fab5a801d356aef049adbf8132e522b8e02:Marketplace.Regulator.Role:Role"
}

// v1/exercise (for OfferRegulatorService)

{
  argument: {
    "customer": string
  },
  choice: "OfferRegulatorService",
  contractId: string,
  templateId: "72390a820b1bbb8dbf035bb070f93fab5a801d356aef049adbf8132e522b8e02:Marketplace.Regulator.Role:Role"
}
3 Likes

Hi @Alex_Matson,
To make it a bit more concrete: What you’re asking for is effectively a DAR → JSON Schema or maybe even openapi generator.

This does not currently exist. The former is relatively easy to build, the latter requires some changes to the JSON API to make it nicely usable: The way openAPI is structured the response and request types can only depend on the URL. So to say that the request type should be a specific template, the template needs to be part of the URL. Otherwise your schema can only say "/v1/create expects a dam-lf value as the payload” which is significantly less useful.

2 Likes

Yes, exactly.

I did make a first attempt at hand-crafting an OpenAPI document to represent the JSON schemas, but ran into the issue you mention where only one path is allowed. Using oneOf for the request content is possible, but becomes much less useful for the purposes of documentation. (Also, not to mention that handwriting schemas for a medium to large Daml application would be quite tedious ;P)

A DAR → JSON Schema generator should suffice for my use case. How should I go about writing one? Should I look at some of the existing code generators (i.e., the JS codegen) for inspiration, and maybe try to modify them? Or is there a simpler way?

Thanks!

2 Likes

The TypeScript code generator is probably a good starting point. The code for that is in here daml/TsCodeGenMain.hs at 993591e53bd6cef1c0a2bb144e9aeda7b00215ea · digital-asset/daml · GitHub. Hopefully writing a JSON schema codegen is much simpler.

3 Likes

For reference, for your Role template, daml codegen js outputs

export declare type Role = {
  operator: damlTypes.Party;
  provider: damlTypes.Party;
  observers: pkg97b883cd8a2b7f49f90d5d39c981cf6e110cf1f1c64427a28a6d58ec88c43657.DA.Set.Types.Set<damlTypes.Party>;
};

// dereferencing Set:
export declare type Set<k> = {
  map: damlTypes.Map<k, {}>;
};

// damlTypes are all hand-written in language-support/ts/daml-types/index.ts
export type Party = string;
export interface Map<K, V> {
  get: (k: K) => V | undefined;
  has: (k: K) => boolean;
  set: (k: K, v: V) => Map<K, V>;
  delete: (k: K) => Map<K, V>;
  keys: () => Iterator<K, undefined, undefined>;
  values: () => Iterator<V, undefined, undefined>;
  entries: () => Iterator<[K, V], undefined, undefined>;
  entriesArray: () => [K, V][];
  forEach: <T, U>(f: (value: V, key: K, map: Map<K, V>) => T, u?: U) => void;
}

So you have a couple differences:

  1. The codegen output takes advantage of TypeScript dereferencing. This includes recursive types, which make it so you can’t naively expand type declarations to get rid of this indirection.
data SomeList a = SomeList { uncons : Optional (a, SomeList a) }
-- makes
export declare type SomeList<a> = {
  uncons: damlTypes.Optional<pkg40f452260bef3f29dede136108fc08a88d5a5250310281067087da6f0baddff7.DA.Types.Tuple2<a, SomeList<a>>>;
};
  1. Most structures in TypeScript match the JSON format, but there are a few exceptions where actual codec-ing is being done, such as with Map.

Nevertheless, it’s a good starting point, as @cocreature said.

2 Likes