Skip to content

Instantly share code, notes, and snippets.

@warborn
Last active October 22, 2018 18:31
Show Gist options
  • Save warborn/8aac4ceec5c8f79ad5d47d31b29eb7d6 to your computer and use it in GitHub Desktop.
Save warborn/8aac4ceec5c8f79ad5d47d31b29eb7d6 to your computer and use it in GitHub Desktop.
A first approach to the development of the Dasboard's feature

Dashboards Spec

Elasticsearch Index

In order to have a flexible schema that will be easy to maintain, the proposed approach is to save a Dashboard as a String that stores a JSON document that can be parsed by GraphQL and at the end by our frontend.

The index representation is the following one:

curl -k -XPUT --silent "http://localhost:9200/dashboards_v1?pretty" -H 'Content-Type: application/json' -d '
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings": {
    "dashboard": {
      "properties": {
        "name": {
          "type": "keyword"
        },
        "owner_id": {
          "type": "keyword"
        },
        "elements": {
          "type": "text"
        },
        "created_at": {
          "type": "date",
          "format" : "yyyy-MM-dd HH:mm:ss"
        },
        "updated_at": {
          "type": "date",
          "format" : "yyyy-MM-dd HH:mm:ss"
        }
      }
    }
  }
}
'

curl -k -XPOST --silent "http://localhost:5001/_aliases?pretty" -H 'Content-Type: application/json' -d '
{
  "actions" : [
    {"add": {"index": "dashboards_v1", "alias": "dashboards"}}
}'

The full representation of a Dashboard will be stored in the elements field.

GraphQL Schema

A GraphQL layer is proposed in order to return dashboard's data and to parse a portion of the JSON document in order to extract the visual elements and allow the client to query them as necessary.

The GraphQL schema is the following one:

DASHBOARD {
  ownerId: ID
  name: String
  createdAt: DateTime
  updatedAt: DateTime
  elements: [DASHBOARD_ELEMENT]
}

DASHBOARD_ELEMENT {
  category: String
  type: String
  distribution: ElementDistribution
  schema: String
  elements: [DASHBOARD_ELEMENT]
}

ELEMENT_DISTRIBUTION {
  width: Int
  position: Int
}

Description

A Dashboard is a type the representation of a dashboard and it can contain any number of Dashboard Elements.

A Dashboard Element is a type that represents a visual element attached to the a Dashboard, each one has a category and a type that can help to organize the types of dashboard elements that we have, a first approach to categorize the elements can be the following one:

  • Categories
    • Insight
      • Types
        • Pie
        • Line
        • Bar
        • Table
        • Treemap
    • Text
      • Types
        • Paragraph
        • Title
        • Subtitle
    • Separator
      • Types
        • Horizontal

The distribution field contains information about the current position of the element over a Dashboard along with the width which can be stored as a number that represents the number of columns of a grid that the element occupies (this is just a proposal).

The schema field is a String that stores a JSON document that represents what is needed in order to display the visual element. In the case of a Chart it contains the configuration and filters used to generate the chart. In the case of a text it contains the content of the text and in the future it can contain more values to represent things like styles. This schema is not meant to be parsed by GraphQL because each Dashboard Element may need different information in order to be rendered and we cannot know ahead of time this information to make a fixed GraphqQL schema.

The elements field is an array of Dashboard Element because in the future we may want to associate elements with other elements like in the case of a Chart that has an associated text and they should be rendered as one element on a Dashboard.

An Element Distribution is a type that represents how a Dashboard Elementìs distributed over a Dashboard in terms of position and width.

Example

A GraphQL schema like this one, will allow the client to query the following data:

query {
	dashboard {
		id
		ownerId
		name
		elements {
			category
			type
			distribution {
				width
				position
			}
			elements {
				category
				type
				distribution {
					width
					position
				}
				schema
			}
			schema
		}
	}
}

The nested selection of the elements field allows us to retrieve visual elements that contains inner elements such as a Chart that has an associated text and they should be rendered together as one Dashboard Element. This logic can be applied to more elements in the future.

Example output of the previous query:

{
	"data": {
		"dashboard": {
			"elements": [
				{
					"category": "text",
					"distribution": {
						"position": 1,
						"width": 6
					},
					"elements": [],
					"schema": "{\"content\": \"Incremento de asaltos\"}",
					"type": "subtitle"
				},
				{
					"category": "text",
					"distribution": {
						"position": 2,
						"width": 24
					},
					"elements": [],
					"schema": "{\"content\": \"Los asaltos han ido incrementando en los estados de ...\"}",
					"type": "paragraph"
				},
				{
					"category": "chart",
					"distribution": {
						"position": 3,
						"width": 12
					},
					"elements": [
						{
							"category": "text",
							"distribution": {
								"position": null,
								"width": null
							},
							"schema": "{\"content\": \"Gr\\u00e1fica de incremento en asaltos\"}",
							"type": "paragraph"
						}
					],
					"schema": "{\"configuration\": {\"sdlocs\": \"country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8\", \"yop\": \"count\", \"yaxis\": \"ccat\", \"interval\": \"year\", \"xaxis\": \"idt\"}, \"filters\": {\"sdt\": \"2018-01-01\", \"edt\": \"2018-05-01\", \"source\": \"internal\"}}",
					"type": "line"
				},
				{
					"category": "chart",
					"distribution": {
						"position": 4,
						"width": 12
					},
					"elements": [
						{
							"category": "text",
							"distribution": {
								"position": null,
								"width": null
							},
							"schema": "{\"content\": \"Gr\\u00e1fica de incremento en asaltos\"}",
							"type": "paragraph"
						}
					],
					"schema": "{\"configuration\": {\"sdlocs\": \"country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8\", \"yop\": \"count\", \"yaxis\": \"ccat\", \"interval\": \"year\", \"xaxis\": \"idt\"}, \"filters\": {\"sdt\": \"2018-01-01\", \"edt\": \"2018-05-01\", \"source\": \"internal\"}}",
					"type": "bar"
				},
				{
					"category": "separator",
					"distribution": {
						"position": 5,
						"width": 24
					},
					"elements": [],
					"schema": null,
					"type": "horizontal"
				},
				{
					"category": "chart",
					"distribution": {
						"position": 6,
						"width": 24
					},
					"elements": [],
					"schema": "{\"configuration\": {\"xdcrimes\": \"category:kidnapping,category:homicide,category:extortion,category:car_theft\", \"yop\": \"count\", \"yaxis\": \"ccat\", \"chart\": \"pie\"}, \"filters\": {\"source\": \"secretariat\"}}",
					"type": "pie"
				}
			],
			"id": "uSqgjmYB1C2l0t2yPeIS",
			"name": "Asaltos 2018",
			"ownerId": "ksj824ba1j1330"
		}
	}
}

Storing a Dashboard

In order to store a Dashboard that looks like this: Dashboard example

The following request can be made to Elasticsearch (http://localhost:9200/dashboards/dashboard/):

{
	"name": "Asaltos 2018",
	"owner_id": "ksj824ba1j1330",
	"created_at": "2018-01-01 00:00:00",
	"updated_at": "2018-01-05 00:00:00",
	"elements": "[{\"category\":\"text\",\"type\":\"subtitle\",\"distribution\":{\"position\":1,\"width\":6},\"schema\":{\"content\":\"Incremento de asaltos\"}},{\"category\":\"text\",\"type\":\"paragraph\",\"distribution\":{\"position\":2,\"width\":24},\"schema\":{\"content\":\"Los asaltos han ido incrementando en los estados de ...\"}},{\"category\":\"chart\",\"type\":\"line\",\"distribution\":{\"position\":3,\"width\":12},\"schema\":{\"configuration\":{\"xaxis\":\"idt\",\"interval\":\"year\",\"sdlocs\":\"country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8\",\"yaxis\":\"ccat\",\"yop\":\"count\"},\"filters\":{\"source\":\"internal\",\"sdt\":\"2018-01-01\",\"edt\":\"2018-05-01\"}},\"elements\":[{\"category\":\"text\",\"type\":\"paragraph\",\"schema\":{\"content\":\"Gráfica de incremento en asaltos\"}}]},{\"category\":\"chart\",\"type\":\"bar\",\"distribution\":{\"position\":4,\"width\":12},\"schema\":{\"configuration\":{\"xaxis\":\"idt\",\"interval\":\"year\",\"sdlocs\":\"country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8\",\"yaxis\":\"ccat\",\"yop\":\"count\"},\"filters\":{\"source\":\"internal\",\"sdt\":\"2018-01-01\",\"edt\":\"2018-05-01\"}},\"elements\":[{\"category\":\"text\",\"type\":\"paragraph\",\"schema\":{\"content\":\"Gráfica de incremento en asaltos\"}}]},{\"category\":\"separator\",\"type\":\"horizontal\",\"distribution\":{\"position\":5,\"width\":24}},{\"category\":\"chart\",\"type\":\"pie\",\"distribution\":{\"position\":6,\"width\":24},\"schema\":{\"configuration\":{\"chart\":\"pie\",\"xdcrimes\":\"category:kidnapping,category:homicide,category:extortion,category:car_theft\",\"yaxis\":\"ccat\",\"yop\":\"count\"},\"filters\":{\"source\":\"secretariat\"}}}]"
}

This Dashboard corresponds to the following JS Object:

{
  "name": "Asaltos 2018",
  "ownerId": "ksj824ba1j1330",
  "createdAt": "2018-01-01 00:00:00",
  "updatedAt": "2018-01-05 00:00:00",
  "elements": [
    {
      "category": "text",
      "type": "subtitle",
      "distribution": {
        "position": 1,
        "width": 6
      },
      "schema": {
        "content": "Incremento de asaltos"
      }
    },
    {
      "category": "text",
      "type": "paragraph",
      "distribution": {
        "position": 2,
        "width": 24
      },
      "schema": {
        "content": "Los asaltos han ido incrementando en los estados de ..."
      }
    },
    {
      "category": "chart",
      "type": "line",
      "distribution": {
        "position": 3,
        "width": 12
      },
      "schema": {
        "configuration": {
          "xaxis": "idt",
          "interval": "year",
          "sdlocs": "country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8",
          "yaxis": "ccat",
          "yop": "count"
        },
        "filters": {
          "source": "internal",
          "startDate": "2018-01-01",
          "endDate": "2018-05-01"
        }
      },
      "elements": [
        {
          "category": "text",
          "type": "paragraph",
          "schema": {
            "content": "Gráfica de incremento en asaltos"
          }
        }
      ]
    },
    {
      "category": "chart",
      "type": "bar",
      "distribution": {
        "position": 4,
        "width": 12
      },
      "schema": {
        "configuration": {
          "xaxis": "idt",
          "interval": "year",
          "sdlocs": "country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8",
          "yaxis": "ccat",
          "yop": "count"
        },
        "filters": {
          "source": "internal",
          "startDate": "2018-01-01",
          "endDate": "2018-05-01"
        }
      },
      "elements": [
        {
          "category": "text",
          "type": "paragraph",
          "schema": {
            "content": "Gráfica de incremento en asaltos"
          }
        }
      ]
    },
    {
      "category": "separator",
      "type": "horizontal",
      "distribution": {
        "position": 5,
        "width": 24
      }
    },
    {
      "category": "chart",
      "type": "pie",
      "distribution": {
        "position": 6,
        "width": 24
      },
      "schema": {
         "configuration": {
            "chart": "pie",
            "xdcrimes": "category:kidnapping,category:homicide,category:extortion,category:car_theft",
            "yaxis": "ccat",
            "yop": "count"
        },
        "filters": {
          "source": "secretariat"
        }
      }
    }
  ]
}

This show us a how a Dashboard can look like while is being use in the frontend, fully parsed and ready to use.

Additional Resources

An implementation of the GraphQL schema in python along with an example of how to retrieve a stored Dashboard in ES that follows the index described above can be found here, this should be considered only a demonstration of the capabilities of the described spec and it's a WIP.

A very basic implementation of how to display a stored Dashboard returned by the API with the query shown above can be found here, this should considered only a demonstration of the capabilities of the described spec and it's a WIP.

@cultome
Copy link

cultome commented Oct 22, 2018

Excellent work bro! The only thing I would change would be the nested elements. For each element type (lets say a Chart) we will have only a limited bunch of expected elements (for this example a title and a chart footer) that would be positioned in a expected way, so it is not necesary to have the same flexibility as the parent component. This way we can describe it as follows:

{
  "category": "chart",
  "distribution": {
    "position": 4,
    "width": 12
  },
  "elements": [
    {
      "type": "title"
      "schema": "{\"content\": \"Gr\\u00e1fica de incremento en asaltos\"}",
    },

    {
      "type": "footer"
      "schema": "{\"content\": \"Grafica que demuestra el alza en los indices delictivos\"}",
    }
  ],
  "schema": "{\"configuration\": {\"sdlocs\": \"country:c68c4d2a-b34e-4a22-a2da-d71bceaa1944,state:b161863c-39fa-4b04-8e11-137f8d9443e8\", \"yop\": \"count\", \"yaxis\": \"ccat\", \"interval\": \"year\", \"xaxis\": \"idt\"}, \"filters\": {\"sdt\": \"2018-01-01\", \"edt\": \"2018-05-01\", \"source\": \"internal\"}}",
  "type": "bar"
},

What do you think?

@warborn
Copy link
Author

warborn commented Oct 22, 2018

Thanks! I like the idea, lets do it this way, as you say there will be only a limited set of nested elements, whatever works best for us is good for me 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment