GraphQL with Python

Overview

Using Python to access the GraphQL interface allows the ability to apply the power and versatility of dynamic programming to LeanIX data.

Additionally, the GraphQL interface allows for the update of objects (e.g. attributes on relations) that may would otherwise not be able via the GUI.

Requirements

This tutorial requires that you have Python and pip package manager installed.

  • A third-party resource for getting up-and-running with Python can can be found here.

  • Please find a resource on installing pip here.

Import Modules and Authentication

The following modules should have already been installed (via pip in the system terminal) in order to run your script :

  • pandas

  • requests

  • json

The head of the script is dedicated to bringing in the necessary Python modules to pull data from LeanIX APIs.

# to manipulate json data to provide in the POST or GET operation.
import json 
# to make REST interfaces work for you.
import requests 
# provies access to json_normalize function to make it easy to convert raw GraphQL response into an exportable pandas DataFrame.
import pandas as pd

📘

Authentication

The first part of script authenticates a session with LeanIX MTM (Multi-tenancy module) and stores a unique access key in a variable to be passed to your API call.

For this, create an API Token generated in your LeanIX work (here [https://dash.readme.io/project/leanix/v4.0/docs/authentication]

api_token = '<your API token from admin area>'
auth_url = 'https://app.leanix.net/services/mtm/v1/oauth2/token' # or something else if you have a dedicated MTM instance - you will know it if that is the case and if you don't just use this one.
request_url = 'https://app.leanix.net/services/pathfinder/v1/graphql' # same thing as with the auth_url

response = requests.post(auth_url,
                         auth=('apitoken', apitoken),
                         data={'grant_type': 'client_credentials'})
response.raise_for_status() # this merely throws an error, if Webserver does not respond with a '200 OK'
access_token = response.json()['access_token']

The access token is now stored in the variable access_token, and will be used later on in the script to authorize the API call.

GraphQL

Start by creating a GraphQL query or utilize the query in the example below. Create and test GraphQL queries in GraphiQL as described here.

Please reach out to the support team in LeanIX for support with honing or developing a specific query or mutation matched to your desired use-case.

🚧

Validate your Query

Proceed only if you know the query is working and doing exactly what you would like and expect it to do.

For the purpose of the rest of the tutorial, the example will focus on retrieving basic information about the Applications in LeanIX:

{
  allFactSheets(factSheetType: Application) {
    totalCount
    edges {
      node {
        id
        name
      }
    }
  }
}

Queries and Data Export

With the working query and the access_token at hand, submit the request:

graphql_query = """{
  allFactSheets(factSheetType: Application) {
    totalCount
    edges {
      node {
        id
        name
      }
    }
  }
}"""

data = {"query" : graphql_query}
json_data = json.dumps(data)
auth_header = 'Bearer ' + access_token
header = {'Authorization': auth_header}
  
response = requests.post(url=request_url, headers=header, data=json_data)
response.raise_for_status()
## format the data in a more user-friendly datagrame object:
data_frame = pd.json_normalize(response.json()['data']['allFactSheets']['edges'])
## export data frame to an excel file in the same directory as the script
data_frame.to_excel('my_graphql_data.xlsx', index=False)

The full version of the query script is below.

If you have problems or encounter errors, please let us know by contacting us via [email protected]

import requests  
import json
import pandas as pd 

api_token = '<your API token from admin area>'
auth_url = 'https://app.leanix.net/services/mtm/v1/oauth2/token' 
request_url = 'https://app.leanix.net/services/pathfinder/v1/graphql' 
## 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']

graphql_query = """{
  allFactSheets(factSheetType: Application) {
    totalCount
    edges {
      node {
        id
        name
      }
    }
  }
}"""

data = {"query" : graphql_query}
json_data = json.dumps(data)
auth_header = 'Bearer ' + access_token
header = {'Authorization': auth_header}
  
response = requests.post(url=request_url, headers=header, data=json_data)
response.raise_for_status()
## format the data in a more user-friendly datagrame object:
data_frame = pd.json_normalize(response.json()['data']['allFactSheets']['edges'])
## export data frame to an excel file in the same directory as the script
data_frame.to_excel('my_graphql_data.xlsx', index=False)

Mutations

Mutations allow for data to be updated. In contrast to a query which pulls information. To learn more about mutations, please see GraphQL Basics and GraphQL Advanced.

Create and throuoghly test the mutation in the GraphQL editor. It can be formatted as a string and submitted to the API in the same manner as a query.

📘

String Interpolation

Note that factsheet_id and factsheet_common are variables which are interpolated programmatically in the mutation query string. This approach allows for iterative updates e.g. iterate through a data structure to accomplish a batch of updates without having to manually adjust the query each time.

factsheet_id = "factsheetID"
factsheet_comment = "testComment"

mutation_string = """
    mutation {
      createComment(factSheetId: "%s", message: "%s", status: ACTIVE) {
        id
      }
    }
  """ % (factsheet_id, factsheeet_comment)


data_mutation = {"query": mutation_string}
json_post = json.dumps(str_post)
response = requests.post(url=request_url, headers=header, data=json_post)
response.raise_for_status()

🚧

Mutation Call Response

Mutations by design do not return data that will be saved and exported. In this case, the response is saved and used to confirm success by calling raise_for_status()

FAQs

  • I am receiving a webserver error code 404. What am I doing wrong?
  • Please check the URL that you are sending the request to. For authentication that is https://app.leanix.net/services/mtm/v1/oauth2/token and for your GraphQL requests this is https://app.leanix.net/services/pathfinder/v1/graphql
  • I am not getting any data back. What is broken?
  • Most of the time this is due to a faulty GraphQL query. Please check your query and verify via GraphiQL (https://app.leanix.net//graphiql) that your query works and returns data. If the issue still persists, please contact [email protected]
  • I want to query/mutate a specific fact sheet. Where do I find its UUID (unique uniform identifier)?
  • Go to the fact sheet in LeanIX and look at the address bar of your browser. The last 36 hex-digits in the address (including the hyphens) are the UUID of the fact sheet, that you can use. Alternatively trigger an Excel export (e.g. in the Inventory, when you have selected a single fact sheet type, like 'Application') and look at the ID column.
  • I received an access_token but when sending the GraphQL query, I am getting an unauthorized.
  • Most likely, your access_token expired. Just get a new one and start your query again. If the issue still persists, please contact [email protected]
  • When installing the modules, I receive an error when trying to install the json package?
  • You are probably on Python 3.4 or later and have json module already installed, therefore, PyPI does not list a json package for your python version anymore.
  • I have installed the modules as described, but when importing them in my Python Script/IPython Notebook, I receive an error stating that there is no such module. What's wrong?
  • You probably have installed the module via pip for a different python distribution (e.g. for python 2.7) while your script or notebook is run with your current Python distribution (e.g. 3.6). Just install the modules again with e.g. pip3 requests pandas.