Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Hihu
Product and Topic Expert
Product and Topic Expert

What?


To get more familiar with Python and also polish my somewhat rusty REST know how I wanted to write some code. Additionally it would be nice if SAP Analytic Cloud is somehow involved.

So I decided to develop a small script to export SAC users with their roles and teams. And yes, I am aware of the option to export users to csv from the UI.



But clicking on a button is not the same as learning how to do it ;o)

Note: We will not produce production quality code, this is only for demonstration purposes.

Prerequisites



  • Access to a SAC tenant and rights to view and change System > Administration > App Integration

  • Basic unterstanding of Python, REST or at least some skills to google for it

  • An editor and python executables - it's up to your choice. What I used see below.


My environment


I use VS Code as it is lean and fast. I prefer to start it up via a batch file where I can set explicitely the PATH without bothering anything else on my PC.

So this looks like that - VSCode.bat:
set PATH=%PATH%;C:\Dev\VSCode;C:\Dev\PortableGit\bin;C:\Dev\Python;C:\Dev\Python\Scripts
cd C:\Dev\Projects
start code .
exit

Please note everything is in subfolders of C:\Dev: VSCode, Git, Python and my projects directory.

I also added Python scripts to my PATH as there is the pip utility. Python is just a portable version.

Reference documentation


https://help.sap.com/viewer/product/SAP_ANALYTICS_CLOUD/release/en-US

There is a chapter Development with the necessary infos:


VS Code extensions


In order to use Python I added some extensions to VS Code and configured them. For an easy start look at https://code.visualstudio.com/docs/python/python-tutorial

My extensions look like that:


Setup in SAC


Log on to your tenant and choose from the menu System > Administration > App Integration, then select "Add a new OAuth client". We need to define a kind of technical user to access the system from our program.



Fill out the dialog:



Select "User Provisioning", and also set a password aka secret.

This results in:


Required URLs


We will need two URLs one - Token URL - can be found right here in App Integration:



The other one is derived from your SAC tenant URL:

https://<your-SAC-system>.<region>.sapanalytics.cloud/api/vi/scim

This endpoint provides access to the user and team API.

SCIM means System for Cross-domain Identity Management which is a specification for managing user identities.

Coding


Additional library


After some research I used the requests module, please have a look at their web site: https://pypi.org/project/requests/2.7.0/

Steps



  1. Request an OAuth access token via POST to token URL and fetch the result

  2. Use this access token to get a x-csrf-token via GET to your tenant and store the result

  3. Use the x-csrf-token for every request, in this case getting a user list


Program structure


To keep things simple I put everything in one file, for better understanding I show it one part after the other.

We need some modules, then the base URLs and logon data are defined in variables.
import json
import requests
import base64

api_oauth_client_id = 'My_OAuth_Client'
api_secrect = 'stefan'
# derived from your SAC tenant URL
api_url_base = 'https://<your tenant>.eu1.sapbusinessobjects.cloud/api/v1/scim/'
# This is from System > Administration > App Integration
api_token_url = 'https://oauthasservices-<your oauth-service>.hana.ondemand.com/oauth2/api/v1/token'

I put step 1 in the function get_api_token():
def get_api_token():
# combine client id and secret according to API
logon = '{0}:{1}'.format(api_oauth_client_id, api_secrect)

# prepare the authorization header, please note the character encoding
token_headers = {'Authorization': 'Basic {0}'.format(
base64.b64encode(logon.encode('utf-8')).decode('utf-8'))}

# prepare POST call parameters
params = {'grant_type': 'client_credentials'}

# call the OAuth API
response = requests.post(api_token_url, headers=token_headers, params=params)

# if success then get the result as json else cry out for help
if response.status_code == 200:
data = json.loads(response.content.decode('utf-8'))
else:
data = None
print('Problem!')

# return json for further processing
return data

Hopefully the inline documentation explains it. After all we do a http post with required headers and get the result as json.

Step 2 is also packaged into a function:
def get_crsf_token():
# maybe ugly but I prepare the http headers outside this function
# please note now we call another URL
response = requests.get('{0}Groups'.format(api_url_base), headers=headers)

# if success then we get a x-crsf-token ...
if response.status_code == 200:
crsf_token = response.headers['x-csrf-token']
else:
crsf_token = None
print('Problem!')

# ... which can be returned
return crsf_token

Now let's put it together and call both functions in a sequence to get the desired x-csrf-token:
# get API token
return_data = get_api_token()

# extract token from json return
api_token = return_data['access_token']

# get crsf token and update headers
headers = {'Authorization': 'Bearer {0}'.format(api_token),
'x-sap-sac-custom-auth': 'true',
'x-csrf-token': 'fetch'}

# get the x-csrf-token and put in into the header for the next request
headers['x-csrf-token'] = get_crsf_token()

If you believe or not, now we are ready to start the real work! Just in case you get lost: We just wanted to export a user list from SAC.
# now lets to do the real work
response = requests.get('{0}Users?count=500'.format(
api_url_base), headers=headers)

# get response as json
if response.status_code == 200:
data = json.loads(response.content.decode('utf-8'))
else:
print('Problem!')

I follow a shortcut here because I extend the page size to a high value so every user in my system is fetched in one request by adding count=500 the the URL. Please see the documention if it's not appropriate to your requirement.

To keep it simple I print the result to stdout by looping over the users and concatenating teams and roles so one line shows a user:
# print(data)
users = data['Resources']
for usr in users:
name = usr['userName']
groups = ''
group_objs = usr['groups']
for grp in group_objs:
groups = groups + grp['display'] + ','
roles = usr['roles']
print('{0} - {1} - {2}'.format(name, groups.rstrip(','), ','.join(roles)))

If you want to run this program in your environment, copy all parts of the demo code into one file, prepare and adapt your SAC tenant and it should work.

Thanks for reading and happy coding!
5 Comments
former_member701856
Participant
0 Kudos
Hi Stefan,

Is it possible to export a SAC model using REST API? If yes, how can I build an URL to get the content of a specific model as a JSON file?

I'm trying to find an alternative way to export a SAC model, since SAC job for OData Services is only available for SAP BW/4HANA 2.0 or BW 750 targets, as described in this post:

https://blogs.sap.com/2019/10/09/export-data-from-sac-model-to-odata-service/

Thanks!
Hihu
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Marcos,

if you want to export the data of the model I'm not aware of an option to do that. The REST API only offers to peak into the metadata as described in

https://help.sap.com/viewer/14cac91febef464dbb1efce20e3f1613/release/en-US/1edb726385c84d91a99a88366...

Basically the URL looks like /api/v1/stories?include=models.

Regards, Stefan
0 Kudos

Hi Stefan,

this blog was a great starting point for me to create my first API calls using Python. Your code works out of the box. However, I wanted to adapt the code to also create users and groups and I found that the CSRF token in your GET request is not really used (CSRF token is optional according to the documentation). When I did POST request I got 403 - Forbidden, because it seems that all requests need to be made in the same session.

After some investigation this seems to be enough:

Create a session at the beginning of the code:

s = requests.Session()

Then in all functions you need to use s.get(...), s.post(...):

response = s.post(url = '{0}Users'.format(api_url_base), data = data, headers = headers)

(For user creation the get_crsf_token function also needs to call the /Users endpoint)

response = s.get('{0}Users'.format(api_url_base), headers=headers)

Best regards,

Harald

Hihu
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Harald,

thanks for sharing! Did you also have a look at https://blogs.sap.com/2020/12/04/how-to-use-rest-api-in-sap-analytics-cloud-to-update-user-profile-i... ?

Kind regards,

Stefan
0 Kudos
Hi Stefan,

yes I saw that and I used Postman to check if user creation works in general, since the Python POST request didn´t work because of what I mentioned.

But I will stick to Python: I want to have a function that gets me all the users with a specific BW role and put them in a team and if they don´t have a user in SAC automatically create the user. I don´t know if I can do that with Postman in one step. I guess I would have to expose the BW user tables over an API somehow, but I don´t have any experience with Postman.

Best regards,
Harald