Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
pieterjanssens
Active Participant
CDS actions and functions are an easy way to respond with a response format other than OData. These are however limited to set of supported types. Unfortunately, Object/JSON is not part of the supported types. If you have a scenario like me (return specific JSON structure from a service to use in a dynamic SuccessFactors tile), where your service needs to return JSON in a specific structure and OData is not an option, then there is still a solution.

Luckily, under the hood, CAP uses express.js as its web application framework and this offers the possibility to serve anything supported by that framework. The authentication of the user seems daunting outside of CDS, but vansyckel pointed out that it's a lot less complex than one might think.

After tweaking Sebastian's example a little bit I ended up with this simple snippet:
const proxy = require('@sap/cds-odata-v2-adapter-proxy')
const cds = require('@sap/cds')
const xsenv = require('@sap/xsenv')
const passport = require('passport')
const JWTStrategy = require('@sap/xssec').JWTStrategy

cds.on('bootstrap', (app) => {
app.use(proxy())
app.get('/favicon.ico', (req, res) => res.status(204))
app.use(
/^\/(v2\/)?(?!ci).*/, //replace 'ci' with the name of your service path
passport.initialize(),
passport.authenticate(
new JWTStrategy(xsenv.getServices({ xsuaa: { tag: 'xsuaa' } }).xsuaa),
{ session: false }
),
(req, res, next) => {
if (!req.authInfo) return res.status(401).end()
//if (!req.authInfo.checkLocalScope('<role>')) return res.status(403).end()
next()
}
)
app.get('/tile', (req, res) => {
// do stuff using the req.user.id and return whatever you'd like
res.json({
tileType: 'sap.sf.homepage3.tiles.DynamicTile',
properties: {
title: 'Flashy Tile',
subtitle: 'Best Run Company',
numberValue: '8.67',
numberState: 'Positive',
numberDigits: 2,
stateArrow: 'Up',
numberFactor: '$',
icon: 'sap-icon://line-chart',
info: 'NASDAQ',
subinfo: 'BSTR'
}
})
})
})
module.exports = cds.server

 

Some things to note here:

  • I'm using a negative lookahead regular expression to apply the authentication strategy on any request that does not match the path of my cds service (or the v2 proxy path)

    • Not sure what path name your service has?

    • The app.use() where the authentication is setup, is only required once. You can have any other number of endpoints such as app.get('/somepath'), app.post('/yikes') or that are also processed (that's what next() makes sure of).



  • This sample shows how this can be combined with the cds odata v2 adapter proxy, this is optional

  • I like to 204 the favicon.ico requests (because I don't need a favicon for my service during testing from a browser + it makes sure this request doesn't hit any breakpoints)

  • You can use req.authInfo.checkLocalScope in any endpoint where you want to check for specific roles

  • I left out the extension of the strategy (to avoid collisions with cds) since the regex of the path where the passport handler is used, is excluding cds requests (I hope vansyckel can confirm this is safe/unsafe and will update the post when he does)


This example shows that CAP can still cover these exceptional scenario's where for some functionality of your app, you really need to return a different data type (or support a different incoming request) besides the built in types supported by cds.

 

 
5 Comments
Labels in this area