Add Line of Business Hierarchy - Aggregations and Dashboards
This sample step-by-step configuration is to expand your Service catalog data in configure8 by adding a higher level entity that is composed of multiple services
Last updated
This sample step-by-step configuration is to expand your Service catalog data in configure8 by adding a higher level entity that is composed of multiple services
Last updated
Copyright © 2023 configure8, Inc. All rights reserved.
In this sample we are also showing how to create custom aggregations and dashboards based on the relations from the new schema, providing a great high level view of all catalog info from the sub-entities.
You can check this video for an overview or follow the step-by-step docs below to setup on yourself.
Logged in with an Admin user, go to the schemas page and "+ Add Schema".
This is where you're defining the fields of your schema. You can customize this data to your organizations needs, by changing, adding or removing fields.
In this sample we are just adding a "Description" field. All the other data and aggregations will come through the services relation.
{
"$schema": "http://json-schema.org/draft-07/schema",
"properties": {
"Description": {
"type": "string"
}
}
}
This is where you define which and how the table columns will be showed in the Line of Business catalog menu.
This is where we build the aggregations that pull data from the relations with Services.
This JSON use the concepts of calculated properties and custom columns formatting. See more about calculated properties here. Full info on how to format columns display here.
[
{
"hidden": true,
"name": "Description",
"columnName": "Description",
"calc": "details.Description"
},
{
"hidden": false,
"name": "openPR",
"columnName": "Open PRs",
"calc": {
"type": "FUNCTION",
"aggregation": "SUM",
"path": "ProdServices.details.openedPR"
},
"format": {
"displayValue": {
"type": "number",
"value": "{self}"
},
"label": {
"type": "tag",
"options": [
{
"color": "orange"
}
]
}
}
},
{
"hidden": false,
"name": "TotalCosts",
"columnName": "Total Costs",
"calc": {
"type": "FUNCTION",
"aggregation": "SUM",
"path": "ProdServices.resources.costs.cost"
}
},
{
"hidden": false,
"name": "DevCosts",
"columnName": "Dev Costs",
"calc": {
"type": "FUNCTION",
"aggregation": "SUM",
"path": "ProdServices.resources.costs.cost",
"filter": [
{
"type": "compare",
"field": "ProdServices.environments.name",
"value": "dev",
"compare": "eq"
}
]
}
},
{
"hidden": false,
"name": "QACosts",
"columnName": "QA Costs",
"calc": {
"type": "FUNCTION",
"aggregation": "SUM",
"path": "ProdServices.resources.costs.cost",
"filter": [
{
"type": "compare",
"field": "ProdServices.environments.name",
"value": "QA",
"compare": "eq"
}
]
}
},
{
"hidden": false,
"name": "ProductionCosts",
"columnName": "Production Costs",
"calc": {
"type": "FUNCTION",
"aggregation": "SUM",
"path": "ProdServices.resources.costs.cost",
"filter": [
{
"type": "compare",
"field": "ProdServices.environments.name",
"value": "production",
"compare": "eq"
}
]
}
},
{
"hidden": true,
"name": "ScorecardMetricResults",
"columnName": "Scorecard checks number",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.scorecard.metrics.id"
}
},
{
"hidden": false,
"name": "ScorecardMetricResultsFail",
"columnName": "Scorecard checks failing",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.scorecard.metrics.id",
"filter": [
{
"type": "compare",
"field": "ProdServices.scorecardMetricResultsServices.pass",
"value": "false",
"compare": "eq"
}
]
}
},
{
"hidden": false,
"columnName": "Checks Passing",
"name": "percentageScorecardsChecks",
"format": {
"displayValue": {
"decimal": 0,
"type": "number",
"value": "{self}%"
}
},
"calc": {
"type": "MATH",
"condition": "*",
"values": [
{
"type": "MATH",
"condition": "/",
"values": [
{
"type": "REGULAR_FUNCTION",
"function": "NVL",
"values": [
{
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.serviceId",
"filter": [
{
"type": "compare",
"field": "ProdServices.scorecardMetricResultsServices.pass",
"value": true,
"compare": "eq"
}
]
},
{
"type": "NUMBER",
"value": 0
}
]
},
{
"type": "REGULAR_FUNCTION",
"function": "NVL",
"values": [
{
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.serviceId"
},
{
"type": "NUMBER",
"value": 0
}
]
}
]
},
{
"type": "NUMBER",
"value": 100
}
]
}
},
{
"hidden": true,
"name": "passingScorecardsChecks",
"columnName": "Scorecards checks passing",
"calc": {
"type": "REGULAR_FUNCTION",
"function": "NVL",
"values": [
{
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.serviceId",
"filter": [
{
"type": "compare",
"field": "ProdServices.scorecardMetricResultsServices.pass",
"value": true,
"compare": "eq"
}
]
},
{
"type": "NUMBER",
"value": 0
}
]
}
},
{
"hidden": true,
"name": "ScorecardMetricResultsPass",
"columnName": "Scorecard checks passing",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.scorecardMetricResultsServices.scorecard.metrics.id",
"filter": [
{
"type": "compare",
"field": "ProdServices.scorecardMetricResultsServices.pass",
"value": "true",
"compare": "eq"
}
]
}
},
{
"hidden": false,
"name": "DORADF",
"columnName": "Deploys",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.ServiceDeploys.id",
"filter": [
{
"type": "compare",
"field": "ProdServices.ServiceDeploys.details.Stop",
"compare": "gte",
"value": {
"context": "startDate"
}
},
{
"type": "compare",
"field": "ProdServices.ServiceDeploys.details.Stop",
"compare": "lte",
"value": {
"context": "endDate"
}
}
]
}
},
{
"hidden": false,
"columnName": "Change Failure Rate %",
"name": "DORACFR",
"calc": {
"type": "MATH",
"condition": "*",
"values": [
{
"type": "MATH",
"condition": "/",
"values": [
{
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.ServiceDeploys.DeployIncidents.id"
},
{
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.ServiceDeploys.id"
}
]
},
{
"type": "NUMBER",
"value": 100
}
]
},
"format": {
"displayValue": {
"type": "number",
"value": "{self}%"
},
"link": "/services/{id}/dora-metrics",
"label": {
"type": "tag",
"options": [
{
"value": "15",
"compare": "less",
"color": "#8CC572"
},
{
"value": "30",
"compare": "less",
"color": "#CCCCCC"
},
{
"value": "30",
"compare": "greater",
"color": "#D67176"
}
]
}
}
},
{
"hidden": false,
"columnName": "Cycle Time(s)",
"name": "DORALTC",
"calc": {
"type": "FUNCTION",
"aggregation": "AVG",
"path": "ProdServices.ServiceDeploys.details.Duration"
}
},
{
"hidden": false,
"columnName": "Time to Restore(s)",
"name": "DORAMTTR",
"calc": {
"type": "FUNCTION",
"aggregation": "AVG",
"path": "ProdServices.ServiceIncidents.details.Duration"
}
},
{
"hidden": false,
"name": "Vulnerabilities",
"columnName": "Vulneratibilties",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.resources.vulnerabilities.id"
},
"format": {
"displayValue": {
"type": "number",
"value": "{self}"
},
"label": {
"type": "tag",
"options": [
{
"color": "red"
}
]
}
}
},
{
"hidden": false,
"name": "eksCluster",
"columnName": "EKS Clusters",
"calc": {
"type": "FUNCTION",
"aggregation": "COUNT",
"path": "ProdServices.resources.id",
"filter": [
{
"type": "compare",
"field": "ProdServices.resources.providerResourceType",
"value": "AWS:EKS:Cluster",
"compare": "eq"
}
]
}
},
{
"hidden": false,
"name": "ServiceNames",
"columnName": "Service Names",
"calc": {
"type": "FUNCTION",
"aggregation": "ARRAY_AGG",
"values": [
{
"type": "REGULAR_FUNCTION",
"function": "OBJECT_CONSTRUCT",
"values": [
{
"type": "STRING",
"value": "id"
},
{
"type": "PATH",
"path": "ProdServices.id"
},
{
"type": "STRING",
"value": "name"
},
{
"type": "PATH",
"path": "ProdServices.name"
}
]
}
]
},
"format": {
"link": "/services/{self.id}",
"displayValue": {
"type": "string",
"value": "{self.name}"
}
}
}
]
This is how the aggregations and formatting will look like once you push data:
Next step is to create relations with the other schemas in 3. Relations -> + Add Relation.
After saving the schema a new menu will be shown in our catalog menu, under the link that is composed with the Schema identifier field. In this case https://app.configure8.io/dnr-line_of_business.
In the section 4. Tabs you can add custom tabs to this schema. In this example it's particularly useful as we have a lot of aggregations and data from the services to build graphs, tables and dashboards.
As an example, you could have:
You can add widgets and build a custom Line of Business dashboard that looks like this:
[
{
"displayType": "card",
"schema": "ProdServices",
"maxItems": 1,
"item": {
"title": "Services",
"endContent": "{{total}}"
}
},
{
"displayType": "card",
"schema": "ProdServices.repositories",
"maxItems": 1,
"item": {
"title": "Repositories",
"endContent": "{{total}}"
}
},
{
"displayType": "card",
"schema": "ProdServices.environments",
"maxItems": 1,
"item": {
"title": "Environments",
"endContent": "{{total}}"
}
},
{
"displayType": "card",
"schema": "ProdServices.resources",
"maxItems": 1,
"item": {
"title": "Resources",
"endContent": "{{total}}"
}
},
{
"displayType": "card",
"schema": "ProdServices.resources.vulnerabilities",
"maxItems": 1,
"item": {
"title": "Vulnerabilities",
"endContent": "{{total}}"
}
}
]
[
{
"displayType": "chart",
"variant": "bar",
"options": {
"datasets": [
{
"schema": "ProdServices.resources",
"data": "{{ item.accruedCosts }}",
"label": "Accrued Cost",
"color": "orange",
"aggregate": {
"field": "environments.name"
}
},
{
"schema": "ProdServices.resources",
"data": "{{ item.priorPeriodCosts }}",
"label": "Prior Period Cost",
"color": "red",
"aggregate": {
"field": "environments.name"
}
}
]
},
"dateRange": {
"enabled": true
}
}
]
[
{
"displayType": "chart",
"variant": "doughnut",
"options": {
"datasets": [
{
"schema": "ProdServices.scorecardMetricResultsServices",
"aggregate": {
"field": "pass"
},
"sort": {
"direction": "ASC",
"field": "value"
},
"color": [
"#D67176"
],
"label": "checks result",
"filters": [
{
"compare": "eq",
"field": "pass",
"type": "compare",
"value": false
}
],
"axisLabel": "Fail"
},
{
"schema": "ProdServices.scorecardMetricResultsServices",
"aggregate": {
"field": "pass"
},
"sort": {
"direction": "ASC",
"field": "value"
},
"color": [
"#8CC572"
],
"label": "checks result",
"filters": [
{
"compare": "eq",
"field": "pass",
"type": "compare",
"value": true
}
],
"axisLabel": "Pass"
}
]
}
}
]
[
{
"displayType": "chart",
"variant": "bar",
"options": {
"datasets": [
{
"schema": "ProdServices",
"axisLabel": "{{ item.name }}",
"data": "{{ item.accruedCosts }}",
"label": "Accrued Cost",
"color": "orange"
},
{
"schema": "ProdServices",
"axisLabel": "{{ item.name }}",
"data": "{{ item.priorPeriodCosts }}",
"label": "Prior Period Cost",
"color": "red"
}
]
},
"dateRange": {
"enabled": true
}
}
]
[
{
"displayType": "chart",
"variant": "line",
"options": {
"datasets": [
{
"schema": "ProdServices.resources",
"data": "{{ item.accruedCosts }}",
"label": "Accrued Cost",
"color": "#86BC25",
"aggregate": {
"field": "costs.date",
"modifier": "by_date"
},
"sort": {
"direction": "ASC",
"field": "value"
}
}
]
},
"dateRange": {
"enabled": true
}
}
]
You can manual insert data in the menu page by + Add New Entity, or push data via configure8 public API.