How to test ApiKey with AppSDK2 app

Document ID : KB000057553
Last Modified Date : 14/02/2018
Show Technical Document Details

Issue

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 = 35632399136; //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.

User-added image
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.


User-added image
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:

User-added image

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:
?apiKey=_abc123

The app should load successfully:

User-added image
IV. Embed the app?

If you would like to test embedding this app see "Embed the App" section in?this 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.html?apiKey=_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:

User-added image
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.


User-added image

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.html?apiKey=_7gZF" width="800" height="500"></iframe>
? ? <iframe src="http://localhost:3000/App-external2.html?apiKey=_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.html?apiKey=_7gZF" width="800" height="500"></iframe>
    <iframe src="http://localhost:3000/App-external2.html?apiKey=_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.

User-added image

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.html?apiKey=_7gZF" width="800" height="500"></iframe>
  </body>
</html>

NOTE: this custom app is available as is. It is not formally supported by CA Agile Central support.
?