GraphQL Example: Application Costs

Learning GraphQL is easiest when using GraphiQL directly in LeanIX as explained e.g. in GraphQL Basics and GraphQL Advanced. For daily use, you often need to embed the calls into a scripting language like Python. GraphQL with Python gives you some ideas on how to set things up.

This page walks you through a concrete use case often seen both in initial setups and bulk updates - to handle application run and management costs which are represented in LeanIX as an attribute on the relation between Application and IT Component.

1. Initial Import

Suppose we have an import XLS with the following structure

Application name

IT Component name

Annual costs

App1

ITC1

125

App1

ITC2

250

App2

ITC1

500

App2

ITC2

1000

The following script then creates the Fact Sheets and the relations. Note that none of the used Fact Sheets must exist before.

import json 
import requests 
import pandas as pd

# Insert your token and domain (e.g. app.leanix.net)
api_token = '<API Token>'
auth_url = 'https://<domain>/services/mtm/v1/oauth2/token' 
request_url = 'https://<domain>/services/pathfinder/v1/graphql' 

# Get the bearer token - see https://dev.leanix.net/v4.0/docs/authentication
response = requests.post(auth_url, auth=('apitoken', api_token), data={'grant_type': 'client_credentials'})
response.raise_for_status() 
access_token = response.json()['access_token']
auth_header = 'Bearer ' + access_token
header = {'Authorization': auth_header}

# General function to call GraphQL given a query
def call(query):
  data = {"query" : query}
  json_data = json.dumps(data)
  response = requests.post(url=request_url, headers=header, data=json_data)
  response.raise_for_status()
  return response.json()

# Function to create an application via GraphQL
def createApplication(name):
  query = """
    mutation {
    createFactSheet(input: {name: "%s", type: Application}) {
      factSheet {
        id
      }
    }
  }
  """ % (name)
  print "Create Application " + name
  response = call(query)
  return response['data']['createFactSheet']['factSheet']['id'] 

# Function to create an IT Component via GraphQL
def createITComponent(name):
  query = """
    mutation {
    createFactSheet(input: {name: "%s", type: ITComponent}) {
      factSheet {
        id
      }
    }
  }
  """ % (name)
  print "Create IT Component " + name
  response = call(query)
  return response['data']['createFactSheet']['factSheet']['id'] 

# Function to create a relation between Application and IT Component with the costs attribute
def createRelationWithCosts(app, itc, costs) :
  query = """
    mutation {
      updateFactSheet(id: "%s", patches: 
      [{op: add, 
        path: "/relITComponentToApplication/new_1", 
        value: "{\\\"factSheetId\\\": \\\"%s\\\",\\\"costTotalAnnual\\\": %s}"}]) {
        factSheet {
          id
        } 
      }
    }
  """ % (itc, app, costs)
  print "Create relation with costs: " + itc + "->" + app + " = " + str(costs)
  call(query)

# Start of the main logic

# 1. Read the input as a CSV
df = pd.read_csv('example.csv')

# 2. Make sure to create all applications, and save the created ids
apps = {}
for appName in df.loc[:, "app"].unique():
  apps[appName] = createApplication(appName)

# 3. Make sure to create all IT Components, and save the created ids
itcs = {}
for itcName in df.loc[:, "itc"].unique():
  itcs[itcName] = createITComponent(itcName)

# 4. Create the relations based on the saved ids
for index, row in df.iterrows():
  createRelationWithCosts(apps[row['app']], itcs[row['itc']], row['costs'])

2. Export to XLS

To export the costs from LeanIX XLS, use a simple GraphQL script, e.g. in GraphiQL.

{
  allFactSheets(factSheetType: Application) {
    edges {
      node {
        ... on Application {
          name
          relApplicationToITComponent {
            edges {
              node {
                factSheet {
                  name
                }
                costTotalAnnual
              }
            }
          }
        }
      }
    }
  }
}

This will give you a result like the following:

{  
   "data":{  
      "allFactSheets":{  
         "edges":[  
            {  
               "node":{  
                  "name":"App1",
                  "relApplicationToITComponent":{  
                     "edges":[  
                        {  
                           "node":{  
                              "factSheet":{  
                                 "name":"ITC1"
                              },
                              "costTotalAnnual":125
                           }
                        },
                        {  
                           "node":{  
                              "factSheet":{  
                                 "name":"ITC2"
                              },
                              "costTotalAnnual":250
                           }
                        }
                     ]
                  }
               }
            },
            {  
               "node":{  
                  "name":"App2",
                  "relApplicationToITComponent":{  
                     "edges":[  
                        {  
                           "node":{  
                              "factSheet":{  
                                 "name":"ITC1"
                              },
                              "costTotalAnnual":500
                           }
                        },
                        {  
                           "node":{  
                              "factSheet":{  
                                 "name":"ITC2"
                              },
                              "costTotalAnnual":1000
                           }
                        }
                     ]
                  }
               }
            }
         ]
      }
   }
}

Now, just copying the result to an online tool like https://konklone.io/json/, or using your company's preferred JSON to XLS / CSV converter will give you a nice spreadsheet.

3. Update

The scenario under 1. is great if you have no data in your workspace. Another typical case is to update existing values. We will assume that you have an input like the following:

Application ID

IT Component ID

Costs

ad6850af-37e8-4f5b-8256-446edda7bdbe

0e674e57-bff8-48a8-b144-26a434e8e415

125

ad6850af-37e8-4f5b-8256-446edda7bdbe

a135099c-ad61-4871-aa1a-3f6404b3d575

250

ab2438f5-8295-456e-b0e2-622b07e62352

0e674e57-bff8-48a8-b144-26a434e8e415

500

ab2438f5-8295-456e-b0e2-622b07e62352

a135099c-ad61-4871-aa1a-3f6404b3d575

1000

The LeanIX IDs could either be obtained by XLS export (see https://docs.leanix.net/docs/import-export-fact-sheet-data) or just by another GraphQL call.

Now, the script is similar to before. We need to fetch all Applications to get the correct relation ID, and will then just execute a GraphQL mutation per row.

import json 
import requests 
import pandas as pd

# Insert your token and domain (e.g. app.leanix.net)
api_token = '<API Token>'
auth_url = 'https://<domain>/services/mtm/v1/oauth2/token' 
request_url = 'https://<domain>/services/pathfinder/v1/graphql' 

# Get the bearer token - see https://dev.leanix.net/v4.0/docs/authentication
response = requests.post(auth_url, auth=('apitoken', api_token),
                         data={'grant_type': 'client_credentials'})
response.raise_for_status() 
access_token = response.json()['access_token']
auth_header = 'Bearer ' + access_token
header = {'Authorization': auth_header}

# General function to call GraphQL given a query
def call(query):
  data = {"query" : query}
  json_data = json.dumps(data)
  response = requests.post(url=request_url, headers=header, data=json_data)
  response.raise_for_status()
  return response.json()

# Read all existing Application - IT Component relations
def getRelations():
  query = """
  {
    allFactSheets(factSheetType: Application) {
      edges {
        node {
          id
          ... on Application {
            relApplicationToITComponent {
              edges {
                node {
                  id
                  factSheet {
                    id
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  """
  response = call(query)
  apps = {}
  for appNode in response['data']['allFactSheets']['edges']:
    appId = appNode['node']['id']
    apps[appId] = {}
    for relationNode in appNode['node']['relApplicationToITComponent']['edges']:
      relationId = relationNode['node']['id']
      itcId = relationNode['node']['factSheet']['id']
      apps[appId][itcId] = relationId
  return apps

# Update the costs attribute on the existing relation
def updateCosts(app, itc, rel, costs) :
  query = """
    mutation {
      updateFactSheet(id: "%s", 
        patches: [{op: replace, 
        path: "/relApplicationToITComponent/%s",
        value: "{\\\"factSheetId\\\": \\\"%s\\\",\\\"costTotalAnnual\\\": %s}"}]) {
        factSheet {
          id
        } 
      }
    }
  """ % (app, rel, itc, costs)
  print "Update costs: " + app + "->" + itc + " = " + str(costs)
  response = call(query)
  print response

# Start of the main program

# 1. Read the input
df = pd.read_csv('exampleUpdate.csv')

# 2. Get the existing relations from LeanIX
apps = getRelations()

# 3. Update the cost attribute for each row
for index, row in df.iterrows():
  updateCosts(row['app'], row['itc'], apps[row['app']][row['itc']], row['costs'])