Skip to Content

Synopsis: In this series of Blogs, I’ll explore how the Web Intelligence RESTful Raylight Web Services allow users to automate and simplify the management, modification, creation and updates to a batch of Web Intelligence documents.  This series will be of interest to Web Intelligence administrators, designers, consultants and power users, to save time and effort and get the most out of your investment in Web Intelligence.

Edit: Stéphane translated the Powershell script below to Python 3 here:  http://scn.sap.com/community/restful-sdk/blog/2014/10/17/scripting-web-intelligence-the-restful-raylight-web-services-with-python-sample

Intro

I work in SAP Support, specializing in SAP BusinessObjects suite of products, specifically Web Intelligence and the Software Development Kit (SDK).

Being a Support Guy, I live to help people out – resolve problems, fix issues, overcome difficulties, go for the happy ending.

But sometimes in this job, you have to deliver a tough message: “You can’t do that”.  “That’s too difficult”.  “Not supported”  “Beyond the product’s capabilities”

Important part of the job, but not something I care to deliver.

One such conversation, that’s happened more times that I care to remember, goes like this:

Customer: “Hey, I need to make a simple change to a Web Intelligence document.”

Support Guy: “That sound simple enough.”

Customer: “But there’s hundreds of these WebI docs that needs this change.”

Support Guy: “Oh.”

Customer: “Any way I can automate the task?  It’ll take me days or even weeks to make changes to all the docs.”

Support Guy: “Sure!  Do you know Java(tm)?”

Customer: “Uh no. I’ve wrangled a bit of VBScript”

Support Guy: “Nope.  Sorry”

Customer: “I used some C# .NET for a project.”

Support Guy: “It’s gotta be Java.  The WebI API for report modification is Java-only…”

Customer “Well, ok.  I learned a bit of Java in college.  I can get by.”

Support Guy: “..in a web application.”

Customer: “Huh?  Why.  It’s Java.  That shouldn’t matter.”

Support Guy: “Well, the WebI API doesn’t care, but you need to access the WebI document from  BI Platform using the Platform SDK.  And that’s only supported in a web application, because of how the Enteprrise Framework CORBA TCP/IP is designed.”

Customer: “Starting to sound complicated.  Ok, so once I can get a Java web application, then I can make simple changes to a WebI doc?”

Support Guy: “Absolutely!  You need to learn the ReportEngine Java, REBean for short.  To get started, you need to become familiar with, say, about 40 classes, and the structure of ReportBlocks, ReportElements, and walk down the report hierarchy.”

Customer: “But all I want to do is make one simple change!  For a few hundred WebI docs, but it’s really simple! Don’t you have any other solution?”

Support Guy: “Well, uh.  You wouldn’t happen to have a Intern over the Summer?  That can go and manually fix up all the reports?”

So the last comment’s a bit in jest.  But I do know customers who’ve done that.  They, their co-workers, their assistants, or temporary help had to go through hundreds of reports to make simple changes in each one.

The REBean API – the one used to implement the Web Intelligence viewer and designer – is extremely powerful, but extremely complicated, and difficult to deploy.  It’s just not made for simple tasks.

I’ve always thought if there was just a simple API, that is supported using any programming language, simple to use, and accessible anywhere, then that would be the coolest – it’ll open up automating WebI talks to the casual coders and power users.

That’s where the Web Intelligence RESTful Raylight Web Services API comes in – it’s WebI scripting that everyone can use, everywhere.

Web Intelligence Scripting for Everyone Everywhere

You can read about Raylight in the SCN Community here: http://scn.sap.com/community/restful-sdk

It’s Simple. It’s Easy to Learn.  It Supports any Programming Language that Supports HTTP, meaning pretty much all programming languages common today. And you can run the program anywhere that has access to the web.  That’s pretty much everywhere nowadays.

And you don’t have to be an expert code wranglers to get something useful out of it.  To give and example, when I first learned REBean API, it took me days till I stopped feeling like I was stumbling and mumbling along.  But when I started learning Raylight, within an hour I was exporting a WebI document to PDF, and by the end of the day, I was doing some pretty nifty stuff – scheduling, modifying document images and pages,changing chart color styles,, batch processing documents, all the good stuff.

And the scripts I was writing to accomplish this was concise, modular, easy to read and understand, and simple to modify.  I can take a ‘cookbook’ approach when trying to accomplish a given task.   

If you’ve looked at any of the RESTful Web Services APIs out there, you’ll know why.  REST is based on HTTP verbs – essentially, Raylight is built on four methods: HTTP GET, POST, PUT and DELETE.  All functional dstinction beyond that is specified via  the URI and content – the HTTP headers, parameters and body.

An exmple:  to log onto the Raylight API, you HTTP POST to http://<host>:<port>/biprws/logon/long, Content-Type application/json and Body {“userName”: “Administrator”, “password”: “Password”, “auth”: “secEnterprise”}, and to export a WebI document to PDF, you HTTP GET to http://<host>:<port>/biprws/raylight/v1/documents/<document SI_ID>/pages with Accept application/pdf.

Pretty intuitive and simple.

Now, the first decision to make is what scripting language to use?  Most scripting languages support REST – Java, .NET, Python, Perl, Ruby, even VBScript.    My decision for this series of blogs came down to these criteria:  (1) economy of expression, (2) feature modern language constructs, (3) generally available, (4) robust support for needed functionality, and (5) ease of use.  One scripting language stood out, particularly when it came to ease of use.

Microsoft Powershell

Powershell version 4 (http://technet.microsoft.com/en-us/library/hh857339.aspx) recently was made public, and that’s what I decided to use.  It’s featureset is impressive –

access to .NET and COM libraries, direct and fully-featured support for REST via the Invoke-RestMethod Cmdlet, and native support for both XML and JSON.  It’s the scripting language and IDE supplied with Microsoft products such as SQL Server to accomplish administrative tasks.

Powershell version 3 had an inadequate implementation of Invoke-RestMethod, particularly the non-support for setting Accept HTTP Headers, but that’s all resolved with version 4. 

The native support for JSON is pretty nify- you can generate JSON structures by dynamically creating a Powershell CustomObject, and have that translated to JSON serialization form.  For example to generate my logon body, I invoke:

$logonInfo = @{}

$logonInfo.userName = “Administrator”   

$logonInfo.password = “Password1”

$logonInfo.auth = “secEnterprise”

$logon = ($logonInfo | ConvertTo-Json)

=>

{

    “username” : “Administrator”,

    “password” : “Password”,

  “auth”: “secEnterprise”

}

So I can dynamically build up objects with the required members, then have Powershell simply translate them to JSON structures that I can then send through to the REST HTTP call.  The Invoke-RestMethod is intuitive:

$result = Invoke-RestMethod -Method <method> -URI <url> -Headers <headers> -Body <body>

where <method> is one of Get, Post, Put or Delete, the <url> is the REST resource URI, the headers are a dictionary of HTTP Header values and <body> represents the string or object to send with the REST call.

Here is how one would log onto Raylight using Powershell:

$logonInfo = @{}

$logonInfo.userName = “Administrator” 

$logonInfo.password = “Password1”

$logonInfo.auth = “secEnterprise”

$logon = ($logonInfo | ConvertTo-Json)

$headers = @{“Accept” = “application/json”;“Content-Type” = “application/json”}

$result = Invoke-RestMethod -Method Post -Urihttp://host:8080/biprws/logon/long -Headers $headers -Body $logon

where I’m sending in the logon credentials to the logon/long resource in Raylight, specifying that the content is in JSON by setting Content-Type to be the JSON MIME type.

I can extract the logon token from the $result and use that for subsequent calls. For example, here is how I would retrieve the PDF export for a WebI document identified by the id SI_ID=2269 and save to a file:

$filePath = “C:\Folder\webidoc.pdf”

$logonToken = “`”” + $result.logonToken + “`””

$headers = @{ “X-SAP-LogonToken” = $logonToken; “Accept” = “application/pdf”}

Invoke-RestMethod -Method Get -Urihttp://host:8080/biprws/raylight/v1/documents/2269/pages” –Headers $headers -OutFile $filePath

I’ve added export_pdf.ps1, a Powershell script, to this Blog post.  It adds a bit more functionality – refresh the document, unload the document from Raylight, and log off.

Have a look!

Summary

I want to take a cookbook approach to scripting Web Intelligence using the Raylight API and Powershell.  In subsequent blogs, I’ll cover the commonly-requested tasks: export, schedule, change styles, add reports, change Universes, etc.

If you have any functionality you’d like me to illustrate, that you’ll think others would also like to see, please let me know, and I’ll see what I can do.

Next entry: http://scn.sap.com/community/restful-sdk/blog/2013/09/06/scripting-web-intelligence-the-restful-raylight-web-services–working-it

Code: export_pdf.ps1


<#
.Synopsis
Exports WebI to PDF.
.Description
Logs onto BI Platform, retrieves PDF export of a Web Intelligence document and save to specified file.
Modify this script to enter the logon credentials, URL to the RESTful Web Services, Language, Document SI_ID and folder path.
#>
############################################################################################################################
# Input: $logonInfo, $hostUrl, $locale, $documentId and $folderPath to suite your preferences
$logonInfo = @{}
$logonInfo.userName = "Administrator"
$logonInfo.password = "Password1"
$logonInfo.auth     = "secEnterprise"
$hostUrl = "http://10.160.206.89:6405/biprws"
$documentId = 7827         # SI_ID for the document
$locale = "en-US"          # Format language for the WebI exporter
$contentLocale = "en-US"   # Format language for the WebI document contents
$folderPath = "C:\Users\I819039\Desktop\RESTful"  # Folder where PDF file will be saved.
############################################################################################################################
$raylightUrl = $hostUrl + "/raylight/v1"
$documentUrl = $raylightUrl + "/documents/" + $documentId
# Logon and retrieve the logon token to be used in subsequent RESTful calls.
$headers = @{"Accept"       = "application/json" ;
             "Content-Type" = "application/json"
            }
$result = Invoke-RestMethod -Method Post -Uri ($hostUrl + "/logon/long") -Headers $headers -Body (ConvertTo-Json($logonInfo))
$logonToken =  "`"" + $result.logonToken + "`""  # The logon token must be delimited by double-quotes.
# Get Web Intelligence document information.
$headers = @{ "X-SAP-LogonToken" = $logonToken ;
              "Accept"           = "application/json" ;
              "Content-Type"     = "application/json";
              "Accept-Language"  = $locale;
              "X-SAP-PVL"        = $contentLocale
              }
$result = Invoke-RestMethod -Method Get -Uri $documentUrl -Headers $headers
$document = $result.document
# Refresh the document by sending empty prompts (assumes document has no prompts).
$headers = @{ "X-SAP-LogonToken" = $logonToken ;
              "Accept"           = "application/json" ;
              "Content-Type"     = "application/json" ;
              "X-SAP-PVL"        = $contentLocale
              }
$parametersUrl = $documentUrl + "/parameters"
$result = Invoke-RestMethod -Method Put -Uri $parametersUrl -Headers $headers
# Retrieve and save PDF first ensuring the file path is valid.
$filePath = $folderPath + "/" + $document.name + ".pdf"
if(Test-Path $filePath -isValid) {
    # Get PDF and save to file
    $headers = @{ "X-SAP-LogonToken" = $logonToken ;
                  "Accept"           = "application/pdf" ;
                  "X-SAP-PVL" = $contentLocale
               }
    Invoke-WebRequest -Method Get -Uri ($documentUrl + "/pages") -Headers $headers -OutFile $filePath
} else {
    Write-Error "Invalid file path " + $filePath
}
# Unload document from Raylight.
$headers = @{ "X-SAP-LogonToken" = $logonToken ;
              "Accept"           = "application/json" ;
              "Content-Type"     = "application/json" ;
              "X-SAP-PVL"        = $contentLocale
              }
$result = Invoke-RestMethod -Method Put -Uri $documentUrl -Headers $headers -Body (ConvertTo-Json(@{"document"=@{"state"="Unused"}}))
# Log off the Session identified by the X-SAP-LogonToken HTTP Header
$headers = @{ "X-SAP-LogonToken" = $logonToken ;
              "Accept"           = "application/json" ;
              "Content-Type"     = "application/json"
            }
Invoke-RestMethod -Method Post -Uri ($hostUrl + "/logoff") -Headers $headers
To report this post you need to login first.

24 Comments

You must be Logged on to comment or reply to a post.

  1. Denys van Kempen

    Interesting blog, Ted

    Well explained and good examples.

    This will certainly help customers, partners and colleagues levering scripting for Web Intelligence. 

    Would you have an example involving SAP HANA?

    (0) 
  2. Sam Dalil

    Ted,

    Your script is great, I tried it and it worked fine. I tried to expand your script by trying to run a Webi report with prompts but was not able to get it to work. Do you have examples that uses Webi reports with prompts? How do I pass prompts to Webi reports.

    Thanks in advance

    (0) 
    1. Ted Ueda Post author

      You PUT in a JSON map structure mapping parameter prompt string to value for the the documentId/parameters resource, as shown below (hope it helps!)

      #
      # Retrieves a specified Web Intelligence document as PDF.
      #
      ############################################################################################################################
      # Input: $logonInfo, $hostUrl, $locale, $documentId and $folderPath to suite your preferences
      $logonInfo = @{}
      $logonInfo.userName = “Administrator”
      $logonInfo.password = “Password1”
      $logonInfo.auth     = “secEnterprise”

      $hostUrl = “http://10.160.198.253:6405/biprws

      # Mapping prompt to prompt value
      $promptValueMap = @{“Enter values for State:” = @(“California”, “DC”) ;
                          “Enter values for Year:”  = 2006 }

      $documentId = 6488  # SI_ID for the document

      $locale = “en-US”         # Product Language
      $contentLocale = “en-US”  # Document Content Formatting Language

      # Folder where PDF file will be saved.  File name will be report_name.pdf
      $folderPath = “C:\Users\I819039\Desktop\RESTful”
      ############################################################################################################################

      # Logon and retrieve Logon Token and add to the HTTP Header we send out on subsequent calls.
      $headers = @{“Accept”       = “application/json” ;
                   “Content-Type” = “application/json”
                    }

      $result = Invoke-RestMethod -Method Post -Uri ($hostUrl + “/logon/long”) -Headers $headers -Body (ConvertTo-Json($logonInfo))

      $logonToken =  “`”” + $result.logonToken + “`””  # The logon token must be delimited by double-quotes.

      # Get document information and use the document name as file name.
      $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                    “Accept”           = “application/json” ;
                    “Content-Type”     = “application/json” ;
                    “Accept-Language”  = $locale    ;
                    “X-SAP-PVL” = $contentLocale 
                 }

      $documentUrl = $hostUrl + “/raylight/v1/documents/” + $documentId
      $result = Invoke-RestMethod -Method Get -Uri $documentUrl -Headers $headers
      $document = $result.document

      # Retrieve and set parameters.
      $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                    “Accept”           = “application/json” ;
                    “Content-Type”     = “application/json” ;
                    “X-SAP-PVL” = $contentLocale 
                    }

      # Retrieve the parameters to specify.
      $parametersUrl = $documentUrl + “/parameters”
      $parametersResult = Invoke-RestMethod -Method Get -Uri $parametersUrl -Headers $headers

      # Given each parameter in the parameters collection, specify the parameter value accorinding to the promptValueMap.
      $parametersResult.parameters.parameter | Where-Object {$_ -ne $null} | ForEach-Object {
          $promptValue = $promptValueMap[$_.name]
          if($promptValue){
              $_.answer.values.value = $promptValue
          }
      }

      $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                    “Accept”           = “application/json” ;
                    “Content-Type”     = “application/json”   ;
                    “X-SAP-PVL” = $contentLocale 
                    }
      $result = Invoke-RestMethod -Method Put -Uri $parametersUrl -Headers $headers -Body (ConvertTo-Json $parametersResult -Depth 10)

      # Get PDF and save to file (only if the file path is valid)
      $filePath = $folderPath + “/” + $document.name + “.pdf”
      if(Test-Path $filePath -isValid) {
          # Get PDF and save to file
          $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                        “Accept”           = “application/pdf”;
                        “X-SAP-PVL” = $contentLocale 
                     }
                    
          Invoke-RestMethod -Method Get -Uri ($documentUrl + “/pages”) -Headers $headers -OutFile $filePath
      } else {
          Write-Error “Invalid file path ” + $filePath
      }

      # Unload document from Raylight
      $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                    “Accept”           = “application/json” ;
                    “Content-Type”     = “application/json” ;
                    “X-SAP-PVL”        = $contentLocale
                    }
      $result = Invoke-RestMethod -Method Put -Uri $documentUrl -Headers $headers -Body (ConvertTo-Json(@{“document”=@{“state”=”Unused”}}))

      # Log off the Session identified by the X-SAP-LogonToken HTTP Header
      $headers = @{ “X-SAP-LogonToken” = $logonToken ;
                    “Accept”           = “application/json” ;
                    “Content-Type”     = “application/json” }
      $logoffUrl = $hostUrl + “/logoff”
      Invoke-RestMethod -Method Post -Uri $logoffUrl -Headers $headers

      (0) 
      1. DC Chuang

        Hi Ted,

        Thanks for sharing this script, could you explain where the promptValue is getting passed to the report?  I see the section where it loops through the report parameters and pipes it to $promptValueMap[$_.name] which gets assigned to $promptValue:

        $parametersResult.parameters.parameter | Where-Object {$_ -ne $null} | ForEach-Object {
            $promptValue = $promptValueMap[$_.name]
            if($promptValue){
                $_.answer.values.value = $promptValue
            }
        }

        However I did not find any command that passes the $promptValue to the report.  There is the  Invoke-RestMethod Put, but you have $parametersResult in the body.

        $result = Invoke-RestMethod -Method Put -Uri $parametersUrl -Headers $headers -Body (ConvertTo-Json $parametersResult -Depth 10)

        Another issue is $promptValueMap[$_.name] does not seem to retrieve the prompt value. I can echo $_.name and it returns “Enter values for State:” , but $promptValueMap[$_.name] returns nothing, yet $promptValueMap[“Enter values for State:”] returns “California” “DC”. Any thoughts?

        Thanks,
        DC

        (0) 
        1. Ted Ueda Post author

          Hello DC,

          The $_ is a reference to the current object within the ForEach-Object context, so the code is directly writing to $parametersResult, and feeding it back via the Invoke-RestMethod.

          Don’t know why you’re getting mapping issues, perhaps the strings aren’t exactly the same (colon, whitespace, etc).

          Sincerely,

          Ted Ueda

          (0) 
          1. DC Chuang

            Thanks for the clarification, got distracted by the new variable, but I see it is sent back via:

              $_.answer.values.value = $promptValue

            With the mapping, I found there was an extra space in the prompt name, though odd why manually putting the name (w/o the extra space) in the brackets worked while $_name in the brackets did not.

            Regards,

            DC

            (0) 
  3. Sam Dalil

    Hi Ted,

    Your Script is great, I am trying to expand the script by trying to run Webi reports that use Date, Numeric or string prompts. I was able to get string and Numeric prompts working ok but cannot get the Date prompts working. Do you have an example that uses Date prompts.

    Thanks in advance

    (0) 
    1. Ted Ueda Post author

      Hello Sam,

      I haven’t looked at Date prompts, usually the same format as you would do in the WebI Web Viewer works, or simple forms such as MM/dd/yyyy.

      Sincerely,

      Ted Ueda

      (0) 
      1. Erisa Rediez

        Hello Ted,

        I think that with Date or DateTime in restful call we have to use the special format “yyyy-mm-ddThh:mm:ss(zzzzzz)”. I created a webi document with two prompts one based on a date dimension and one with a datetime dimension. Both lov (get by /parameters) have the above format.

        My database is Oracle and there is one type for date, datetime and time so perhaps this explains that date and datetime whose the same format in restful call.

        (0) 
        1. Ted Ueda Post author

          Hello Erisa,

          As long as the parameter answer type comes back as “DateTime” and not “Text”, that should be the format. The 4.1 SP04 and up versions of the documentation should hopefully clarify that point.

          Thanks for your comment!

          Sincerely,

          Ted Ueda

          (0) 
  4. Doug Armantrout

    Hi Ted,

    When it comes to providing prompt values, is there any way to provide prompt values and request the report in the same API call?  If not, does the restful web service use some mechanism to prevent concurrent requests from getting mixed up?  Perhaps the refresh of the report is tied to a particular logon token as opposed to being a global refresh?

    Thanks.

    (0) 
    1. Ted Ueda Post author

      Hello Doug,

      I think a good way to look at it is that in the RESTful philosophy, everything you can modify is a resource, and document and the parameters associated with the document are different resources.

      It does get a bit tricky, since Raylight still has to keep WebISession state, and from Raylight there’s not a direct way to specify which WebISession you’d use if you’re using multiple.

      Sincerely,

      Ted Ueda

      (0) 
      1. Doug Armantrout

        Thanks for the response Ted.

        I am accessing the RESTful service through a .NET application that may be supporting multiple simultaneous users.  Do you have any advice for supporting near-simultaneous report requests?  For example, if I have a report that takes a Client ID as a parameter, and Client A and Client B make near-simultaneous calls, do you know of any ways to ensure that Client A does not receive a report with Client B’s data and vice-versa?

        Thanks again.

        (0) 
        1. Ted Ueda Post author

          Well, ReportEngine didn’t support multi-threading multi WebISession either, just as a reference point.

          Certainty would be to use different X-SAP-LogonToken for each WebI Session.  Last I checked, it didn’t increase CAL.

          Sincerely,

          Ted Ueda

          (0) 
    1. Ted Ueda Post author

      That’s cool!

      I used to do a whole bunch of python scripts years ago, which is a small part of the reason I chose Powershell for this blog series – to learn a new scripting language 🙂

      I think it’s great how Raylight allows for scripting solutions for WebI.

      Sincerely,

      Ted Ueda

      (0) 
    1. Ted Ueda Post author

      Thanks!

      By the way, Steve Fredell’s one of my managers now.  He was a go-to guy for authentication issues.

      Regards,

      Ted Ueda

      (0) 
  5. Tomas Kratochvíl

    Hi Ted/All

    first of all, your web regarding RESTful is brilliant..I went through the script, especially part of containing prompt values and tried to understand how could be work in case of prompt values not having LOV…as it seems to be designed with prompt values LOV, am I right?

    Could you or anyone please advise me how should be designed code in case of having one promt value with LOV and one without LOV? PromptValueMap will be same..I have attached XML structure of report parameters…

    https://dl.dropboxusercontent.com/u/109516449/prompt.txt

    Regards,

    Thomas

    (0) 

Leave a Reply