How to test ApiKey with AppSDK2 app
search cancel

How to test ApiKey with AppSDK2 app

book

Article ID: 57553

calendar_today

Updated On:

Products

Rally On-Premise Rally SaaS

Issue/Introduction

There is a detailed article Developing and embedding an AppSDK2 app externally that walks through the entire app development process.
The text below is intended for a quick test of your ApiKey with an existing app code example, PI Burnup Chart that is available from AppSDK2 documentation.
This article assumes that a valid ApiKey has been already created in CA Agile Central Application Manager. Documentation for API Key is available in CA Agile Central help.

 

Resolution

I. Prepare local folder:

1. Create a local folder on your workstation. In this example test folder is created on the Desktop.

2. Copy and paste the code example PI Burnup Chart that is available from AppSDK2 documentation to the text editor of your choice.

3. Make sure to make two changes to the code:

replace the placeholder ObjectID of a portfolioitem used in the example with a valid ObjectID of your portfolioitem.
The change should be made on line 66:
var PI_OID = <PI_OID>; //The ObjectID of the PI on which to burn

replace the relative URL in the <script> tag to a full URL:
<script type="text/javascript" src="https://rally1.rallydev.com/apps/2.0/sdk.js"></script>

4. Save the html file to the local folder created in step 1.

II. Start HTTP Server:

There are different ways to start a local http server. In this example python SimpleHTTPServer is started on port 3000.

1. Open a terminal window and cd to the local folder. Make sure the App-external.html is there.

2. Start the server.

$ cd Desktop/test
$ ls
App-external.html
$ python -m SimpleHTTPServer 3000
Serving HTTP on 0.0.0.0 port 3000 ...


III. Test in the browser in incognito mode :

1. Start the browser in incognito mode. Make sure you are not logged in to CA Agile Central in another incognito window.

2. Click on the App-external.html. You should see a prompt to login.
IMPORTANT: we have to make make sure that cached credentials are not used. If you are not presented with the login prompt, close your incognito window, stop the server, and see if you are logged in to CA Agile Central in another browser window. Log out of CA Agile Central from other browser windows and start http server again, as described in the previous section.


Assuming that you are prompted to login as the screenshot shows, click "Cancel". We do not want to use basic authentication.
Open Developer Tools console. You should see 401 error:

After that we established that cached credentials are not used we can move on to the next step, to use apiKey parameter to load the app.

3. Append your ApiKey to the URL following this format:
<API Key - include the underscore before the key>

The app should load successfully:

IV. Embed the app

If you would like to test embedding this app see "Embed the App" section inthis guide. The steps below illustrate the process in detail.

1. Create a new html file, e.g. App-embedded.html, that follows this syntax.

<html>
  <head>
    <title>Embedded app test</title>
  </head>
  <body>
    <iframe src="http://localhost:3000/App-external.htmlapiKey=_7gZF" width="800" height="500"></iframe>
  </body>
</html>


2. Open the terminal. Verify that the new html file is in the same folder where App-external.html is located. Start http server:

$ cd Desktop/test
$ ls
App-embedded.html	App-external.html
$ python -m SimpleHTTPServer 3000
Serving HTTP on 0.0.0.0 port 3000 ...


3. Open browser in incognito mode, go to localhost:3000:

4. Open another tab in the same incognito browser window and go to rally1.rallydev.com to make sure you are going to be prompted to login. We want to make sure that cached credentials are not used in this test. You should see login page. Do not login. Cancel or close that page.

5. Go back to the localhost:port tab. Click on App-embedded.html. App-external.html should load inside the iframe and authenticate with the ApiKey without prompting a user to login.

V. Embed multiple apps

Let's say we want to display a grid of PortfolioItems and PI Burnup chart. Here is an example of App-embedded.html file with two separate iframes. Each iframe references a separate app.

<html>
 <head>
  <title>Embedded app test</title>
 </head>
 <body>
  <iframe src="http://localhost:3000/App-external.htmlapiKey=_7gZF" width="800" height="500"></iframe>
  <iframe src="http://localhost:3000/App-external2.htmlapiKey=_7gZF" width="800" height="500"></iframe>
 </body>
</html>


Note the limitation of this scenario: the two apps are independent. If the goal of this scenario is to share scope and to allow a user action in one app to trigger some other action in the second app, for example, a selection of a PortfolioItem in the grid to trigger loading of the the selected PI's burnup, two independent iframes is not the answer.

It may also seem intuitive to have App-embedded.html file to have code with CA Agile Central namespace in it, as shown in a code fragment below and authenticate the App-embedded.html using ApiKey parameter apiKey=_7gZF....

Note: this will not work!

<html>
  <head>
    <title>Embedded app test</title>
   <script type="text/javascript" src="https://rally1.rallydev.com/apps/2.0/sdk.js"></script>
  <script type="text/javascript">
       CA Agile Central.onReady(function() {
                  Ext.create('CA Agile Central.data.wsapi.Store', {
                   //.......
  </head>
  <body>
    <iframe src="http://localhost:3000/App-external.htmlapiKey=_7gZF" width="800" height="500"></iframe>
    <iframe src="http://localhost:3000/App-external2.htmlapiKey=_7gZFB" width="800" height="500"></iframe>
  </body>
</html>


This will result in"Uncaught TypeError: b.getIoProvider(...).self.getSecurityToken is not a function" because when an app inside the iframe is embedded inside a window with CA Agile Central namespace, AppSDK2 assumes the app running inside CA Agile Central, and not externally.

The solution here is to combine two apps into one, whenever possible. Here is an example of a CA Agile Central app that loads a grid of PortofolioItems. When a user clicks on a row, the PI on that row is selected and PI Burnup chart of that PI loads underneath the grid.

Here is the code for App-external.html file:

<!DOCTYPE html>
<html>
<head>
    <title>PI Grid and Burnup</title>

    <script type="text/javascript" src="https://rally1.rallydev.com/apps/2.0/sdk.js"></script>

    <script type="text/javascript">
        CA Agile Central.onReady(function () {
                var PI_OID = '';

Ext.define('MyCalculator', {
    extend: 'CA Agile Central.data.lookback.calculator.TimeSeriesCalculator',
    config: {
        completedScheduleStateNames: ['Accepted']
    },

    constructor: function(config) {
        this.initConfig(config);
        this.callParent(arguments);
    },

    getDerivedFieldsOnInput: function() {
        var completedScheduleStateNames = this.getCompletedScheduleStateNames();
        return [
            {
                "as": "Planned",
                "f": function(snapshot) {
                    if (snapshot.PlanEstimate) {
                        return snapshot.PlanEstimate;
                    }

                    return 0;
                }
            },
            {
                "as": "PlannedCompleted",
                "f": function(snapshot) {
                    if (_.contains(completedScheduleStateNames, snapshot.ScheduleState) && snapshot.PlanEstimate) {
                        return snapshot.PlanEstimate;
                    }

                    return 0;
                }
            }
        ];
    },

    getMetrics: function() {
        return [
            {
                "field": "Planned",
                "as": "Planned",
                "display": "line",
                "f": "sum"
            },
            {
                "field": "PlannedCompleted",
                "as": "Completed",
                "f": "sum",
                "display": "column"
            }
        ];
    }
});


Ext.define('CustomApp', {
    extend: 'CA Agile Central.app.App',
    componentCls: 'app',
    requires: [
        'MyCalculator'
    ],
    launch: function() {
       this.add({
            xtype: 'rallygrid',
            columnCfgs: [
                'FormattedID',
                'Name',
                'Owner',
                'PercentDoneByStoryCount',
                'PercentDoneByStoryPlanEstimate'
            ],
            context: this.getContext(),
            enableEditing: false,
            showRowActionsColumn: false,
            storeConfig: {
                model: 'portfolioitem/feature'
            },
            listeners:{
                select: this.prepareChart,
                scope: this
            }
        });
    },
    prepareChart:function(selModel, record, rowIndex, options){
        PI_OID = record.get('ObjectID');
        console.log('PI_OID', PI_OID);
        
        this.add({
            xtype: 'rallychart',
            storeType: 'CA Agile Central.data.lookback.SnapshotStore',
            storeConfig: this._getStoreConfig(),
            calculatorType: 'MyCalculator',
            calculatorConfig: {
                completedScheduleStateNames: ['Accepted', 'Released']
            },
            chartConfig: this._getChartConfig()
        });
    },
     _getStoreConfig: function() {
        return {
            find: {
                _ItemHierarchy: PI_OID,
                _TypeHierarchy: 'HierarchicalRequirement',
                Children: null
            },
            fetch: ['ScheduleState', 'PlanEstimate'],
            hydrate: ['ScheduleState'],
            sort: {
                _ValidFrom: 1
            },
            context: this.getContext().getDataContext(),
            limit: Infinity
        };
    },

    /**
     * Generate a valid Highcharts configuration object to specify the chart
     */
    _getChartConfig: function() {
        return {
            chart: {
                defaultSeriesType: 'area',
                zoomType: 'xy'
            },
            title: {
                text: 'PI Burnup'
            },
            xAxis: {
                categories: [],
                tickmarkPlacement: 'on',
                tickInterval: 5,
                title: {
                    text: 'Date',
                    margin: 10
                }
            },
            yAxis: [
                {
                    title: {
                        text: 'Points'
                    }
                }
            ],
            tooltip: {
                formatter: function() {
                    return '' + this.x + '<br />' + this.series.name + ': ' + this.y;
                }
            },
            plotOptions: {
                series: {
                    marker: {
                        enabled: false,
                        states: {
                            hover: {
                                enabled: true
                            }
                        }
                    },
                    groupPadding: 0.01
                },
                column: {
                    stacking: null,
                    shadow: false
                }
            }
        };
    }
});


            CA Agile Central.launchApp('CustomApp', {
                name:"PI Grid and Burnup",
	            parentRepos:""
            });

        });
    </script>



    <style type="text/css">
        .app {
  /* Add app styles here */
}

    </style>
</head>
<body>
</body>
</html>


Here is the App-embedded.html file:

<html>
  <head>
    <title>Embedded app test</title>
  </head>
  <body>
    <iframe src="http://localhost:3000/App-external.htmlapiKey=_7gZF" width="800" height="500"></iframe>
  </body>
</html>


NOTE: this custom app is available as is. It is not formally supported by Rally support.