README.md

# Goldorin

Goldorin is the language that is used to generate GraphQL schema, notation, resolver, related db schema, migration and slate documentation. It is used widely inside arcblock for internal and external APIs.

## How to use

Write your schema definition using Goldorin language, and then parse and generate code with Goldorin toolkit:

```elixir
defmodule MyApp.CodeGen do
  @moduledoc false
  alias Goldorin.Adapter.{Checker, DbMigration, DbRepo, DbSchema, GqlNotation, GqlSchema, Slate}
  alias Goldorin.Parser

  app_name = :myapp
  schemas = [:your_schema_1, :your_shcema_2]
  cwd = Application.app_dir(app_name)
  target_path = Path.join(cwd, "_gen")
  path = Path.join(target_path, "priv/gen")
  doc_path = Path.join(target_path, "api_docs")

  data =
    schemas
    |> Enum.map(fn schema -> Path.join(cwd, "priv/docs/#{schema}/main.yml") end)
    |> Parser.parse()

  # db related
  DbRepo.gen(data, app_name, [{:path, Path.join(path, "db/repo")} | opts])
  DbSchema.gen(data, app_name, [{:path, Path.join(path, "db/schema")} | opts])
  # Note this only generate initial migration. You shall manually create increment migrations yourself.
  DbMigration.gen(data, app_name, [{:path, Path.join(path, "db/migration")} | opts])

  # GraphQL related
  GqlNotation.gen(data, app_name, [{:path, Path.join(path, "gql/notation")} | opts])
  GqlSchema.gen(data, app_name, [{:path, Path.join(path, "gql/schema")} | opts])

  # slate doc. To generate slate doc, please run "mix goldorin.gen.doc"
  Slate.gen(data, app_name, path: doc_path)
  Checker.gen(data, app_name, path: Path.join(path, "checker"))
end
```

Then in your application start code, add this to auto check if a specific resolver is defined properly:

```elixir
MyApp.Goldorin.ensure_resolver_defined()
```

## Goldorin Language

Goldorin uses yaml to define the schema. The main definition is usually called `main.yml`, which looks like this:

```yaml
- name: ethereum
  base_apis:
    - ../base/api.yml
  types:
    - pagination.yml
    - ../base/interface.yml
    - basic.yml
    - complex.yml
  apis:
    - queries.yml
    - mutations.yml
    - subscriptions.yml

```

A type looks like this:

```yaml
- name: PageInfo
  type: output # default
  fields:
    - name: total
      type: integer
      doc: The total number of records accross all pages.
    - name: cursor
      type: string!
      doc: The cursor of the last record in this page.
    - name: next
      type: boolean!
      doc: To indicate if there are more pages afwards.
  meta: resolver=page_resolver
  doc: The pagination information.

- name: PagingInput
  type: input
  fields:
    - name: size
      type: int!
      default: 10
      doc: Returns first several records specified by this parameter.
    - name: cursor
      type: string
      doc: Returns records after the cursor specified by this parameter.
  doc: The common arguments for quering data with pagination.
```

Example of base type:

```yaml
- name: Account
  abstract: true
  doc: The Account interface represents participants of transactions in a blockchain.
  fields:
    - name: address
      type: api=string db=varchar(100)
      meta: pk=true col=address null=false
      doc: The address generated based on the public key of the account.

    - name: pub_key
      type: api=string db=varchar(130)
      meta: null=false
      doc: The public key associated with this account.

    - name: balance
      type: api=bigint db=bigint
      meta: null=false
      doc: The balance of this account.

    - name: total_received
      type: api=bigint db=bigint
      meta: resolver=false
      doc: The total amount received by this account.

    - name: number_txs_received
      type: api=integer db=integer
      meta: null=false
      doc: The number of transactions where the account is a receiver.

    - name: total_sent
      type: api=bigint db=bigint
      meta: null=false
      doc: The total amount sent by this account.

    - name: number_txs_sent
      type: api=integer db=integer
      meta: null=false
      doc: The number of transactions where the account is a sender.
```

Example of inherited type:

```yaml
- name: BitcoinAccount
  base: Account
  abstract: false
  fields:
    - name: script_type
      type: api=string db=varchar(20)
      meta: null=false
      doc: The type of this address.

    - name: sub_keys
      type: api=[string]
      meta: resolver=account_sub_keys
      doc: The subsidiary public keys associated with this addres, normally for multi-signature addresses.

    - name: txs_received
      type: PagedBitcoinTransactions
      doc: The transactions where the account is a receiver.

    - name: txs_sent
      type: PagedBitcoinTransactions
      doc: The transactions where the account is a sender.

  meta: table_name=bitcoin_account
  doc: The BitcoinAccount object implements the Account interface for Bitcoin chain.
```

Example of custom scalar type:

```yaml
- name: EthereumBlock
  base: Block
  abstract: false
  fields:
    - name: total
      type: api=custom_big_number db=decimal
      meta: null=false
      doc: The total amount of ether transacted in this block.
```

## Workflow of Goldorin

Interpreting main.yml

![](https://github.com/ArcBlock/goldorin/blob/master/docs/images/interpreting_main.png)

Interpreting APIs

![](https://github.com/ArcBlock/goldorin/blob/master/docs/images/interpreting_apis.png)

Interpreting Types

![](https://github.com/ArcBlock/goldorin/blob/master/docs/images/interpreting_types.png)


## Syntax

```yaml

- name: the name of api, interface or params.
  e.g.  name: block_by_hash, or name: block or hash.

- doc: the description of return value or params
  e.g.  doc: returns a block by it’s hash, or  doc: he hash of the block.

- return: the return data type of the apis.

- meta: the info of the api, interface or the params
  e.g.  meta: null=false, resolver=false, pk=true

- fields: the set of the data type field in db or GraphQL schema.

- base: the fundamental api or interface.
  e.g.  - name: block_by_hash
          base: block_by_hash
          return: bitcoin_block

- abstract: for those base types, abstract equals to true indicates the data type will not be instantiated.  Default value is false.
  e.g.  - name: block
          abstract: true
```