commontoolsinc / synopsys

datastore service for datums
0 stars 1 forks source link

Synopsys

Synopsys is proof of concept datastore service. It stores facts in terms of entity attribute value triples and allows clients to subscribe to (datomic inspired) queries pushing updates to them when new transactions affect results.

image

Usage

Start

You can start a storage service by running following command in your terminal

npm start

ℹ️ You can optionally configure port service is going to listen to and path where the data will be persisted via PORT and STORE environment variables.

PORT=8080 STORE=/tmp/synopsys npm start

Transact

You can submit transaction to the store via HTTP PATCH request in DAG-JSON format.

const demoTransact = async (url = '///localhost:8080/') => {
  const groceries = {"/":"bafyr4iceqxmgq2pgahy5dkztvrqj65ljzebwpg6ytv6nio3ilyq6rymcy4"}
  const milk = {"/":"bafyr4ifmahtfsopilbtqutqsjjmbyfsid5nggvd7uozbceydeiwkpjs75y"}
  const eggs = {"/":"bafyr4ibll3abnnvtuk6uiakfogixv46r72p2s5lvtbkuqzyd2vfhl5t25u"}
  const bread = {"/":"bafyr4igxo65tssyr4y2wpkjxsphkmlxfnbhmesgusaboeagt5hiykq6xte"}

  const chores = {"/":"bafyr4iathncq5urfqnwbrg2xxudqxanmtmjqobcd2tdps7ogrnoz6su3bu"}
  const dishes = {"/":"bafyr4igg7h46r2koxaslitrd4uhkttfzgr55cm5kziajtrqvyvcqnu2mvq"}
  const laundry = {"/":"bafyr4iaafoljbvwcx4ogb5rftzpampyf7vw6ffgnqmffbu562hpoei676e"}

  const response = await fetch('///localhost:8080/', {
    method: 'PATCH',
    body: JSON.stringify([
      { Assert: [groceries, 'name', 'Groceries'] },
      { Assert: [groceries, 'todo', milk] },
      { Assert: [milk, 'title', 'Buy Milk'] },
      { Assert: [groceries, 'todo', eggs] },
      { Assert: [eggs, 'title', 'Buy Eggs'] },
      { Assert: [groceries, 'todo', bread] },
      { Assert: [bread, 'title', 'Buy Bread'] },
      { Assert: [bread, 'done', true] },
      { Assert: [chores, 'name', 'Chores'] },
      { Assert: [chores, 'todo', laundry] },
      { Assert: [laundry, 'title', 'Do Laundry'] },
      { Assert: [chores, 'todo', dishes] },
      { Assert: [dishes, 'title', 'Do Dishes'] },
    ])
  })

  return await response.json()

Running demoTransact() in your browser you should get commit info like the one below. It tells you what was the merkle root of the store before this transaction and what is it after.

{ 
  "ok": {
    "before": { "id": "NcuV3vKyQgcxiZDMdE37fv" },
    "after": { "id": "Kize9wmtPCCVp1xL9wPPwd" }
  }
}

Query

You can subscribe to a query and receive updates on transactions affecting it. To do that you can submit query via HTTP PUT request in DAG-JSON format. You will get a HTTP 303 redirect to the EventSource URL where updates will be delivered.

const demoQuery = async function* (url = '///localhost:8080/') {
  const request = await fetch('///localhost:8080', {
    method: "PUT",
    body: JSON.stringify({
      select: {
        id: "?list",
        name: "?name",
        todo: [{
          "id": "?item",
          "title": "?title",
          "completed": "?done"
        }]
      },
      where: [
        { Case: ["?list", "name", "?name"] },
        { Case: ["?list", "todo", "?item"] },
        { Case: ["?item", "title", "?title"] },
        {
          Or: [
            { Case: ["?item", "done", "?done"] },
            {
              And: [
                { Not: { Case: ["?item", "done", "?done"] } },
                { "Is": ["?done", false] }
              ]
            }
          ]
        }
      ]
    })
  })

  const reader = request.body.getReader()
  const utf8 = new TextDecoder()
  while (true) {
    const read = await reader.read()
    if (read.done) {
      break
    } else {
      const [id, event, data] = 
        utf8.decode(read.value).split('\n')

      yield {
        id: id.slice('id:'.length),
        event: event.slice('event:'.length),
        data: JSON.parse(data.slice('data:'.length))
      }
    }
  }
}

ℹ️ You could also use EventSource API instead of following HTTP redirect. In that case you could pass redirect: "manual" to fetch request and then derive subscription URL locally from your query via DB.Link.of(query) function.