Technical Articles
Success Factors API Integration with SAP on Premise
Hi!
I’ve decided to create this blog post for two main reasons. First is, like many other posts out there, I’ve struggled a lot to find a solution to a problem I was facing, and I could not find anything out there that helped me in any way, so I want to make sure that if anyone else has the same problem, there is something online that hopefully will help them. Second reason is, I am really not sure of what I am doing, and I am hoping that someone will read this post and will stop me from doing a crazy thing 🙂
I have an UI5 portal connected to an SAP Box (on Premise) and a Success Factors system. Users can navigate from UI5 portal to Success Factors using SSO, but they only get to the landing page. If an user has a TODO item in Success Factors, the only way for them to know is to log in to the UI5 portal, and then navigate to Success Factors (I’m going to say SFSF from now on). In the UI5 portal the users have an Inbox, where they can see if they have pending TODOs, but this inbox is only being fed from SAP right now.
What I am trying to achieve is a way to feed this inbox from SFSF as well, so that they can see their TODOs in there and navigate to the TODO upon clicking on it.
SFSF has its API which I would like to use, and I could pretty much instantiate an OData model from my UI5 app and query that API, but there are a couple of issues around that:
- Both systems are connected through SSO, so I don’t have an userId and password to pass on that call. Even if I had it, I really don’t want to have that info in my controller.
- Both systems have different domains, so I would have to disable CORS on that call and I don’t want to do that either.
I think these two statements above will present great security risks and that is the reason why I am looking for alternatives.
So I figured, if SFSF has an OData API, why cant I just somehow declare that as a service in my SAP Gateway and redirect the call to SFSF using an HTTP destination? This brought me to something called Gateway-OData Consumption and Integration ( package /IWBEP/OCIÂ ). This thing here allows me to generate an OData service in my own gateway using an external service’s metadata ( it could be SFSF, or it could be literally ANYTHING else, OData of course ).
Here is what I did:
- Created a new HTTP destination pointing to my SFSF instance:
/odata/v2 is important here, although you could go as far as pointing to a single entity, if that’s all you need.
- On the gateway, created a new System Alias, using the newly created RFC Destination:
- Next step is to create an empty service, and to register this service in your gateway against the newly created System alias:
At this point your OData service is “connected” to SFSF and now we have to build the service structure using SFSF OData metadata.
- Click on Redefine -> OData Service (SAP GW)
This is going to ask you for some information:
What I did here is do a full overwrite, specify the technical service name and version, and ask the tool to get the metadata using the RFC destination I created earlier.
- Click next.
The tool will try to perform the action, and unfortunately you will get a lot of errors… I don’t have a screenshot to share with you about that, but I guess you will find out for yourself. I will briefly say that, although I don’t know the exact reason as to why these errors come up, it has to do with the fact that SFSF API is huge, and it has a ton of things that don’t translate well when you do the import (function imports and entities with unknown data types etc.), but nothing that cannot be solved with a bit of debugging and getting rid of what you don’t need. I will explain that at the bottom, for now I will continue assuming there are no errors.
In my case, I am interested on the entity TodoEntryV2 and User, as that’s all I really need to cover my scenario, so I did the necessary modifications on OCI (will show later) to only retrieve what I want, and this is what it looks like:
As you can see I only have the two entities I want and the associations between them:
- Now you generate your service and you are done. Lets make a test:
Your newly created OData service is ready to query SFSF and is fully capable, as long as you’ve imported the entities, associations and navigation properties you need.
For my scenario this is a big help, as I can simply use this service that is hosted in the same domain as all my other services and my portal, and I don’t have to worry about CORS, or authorizations, all that works behind the scenes.
My main concern is that due to all errors and al modifications I had to do in standard classes to make this work (don’t worry, more on that below), it makes me feel that I am bending a tool that wasn’t really thought to do this, and I might be getting into somewhere I shouldn’t be getting into. I really hope someone reading this is able to shed some light here.
Ok, lastly I will walk you through the changes I had to make in OCI classes to make this work. Basically my changes consist in preventing these classes from reading and loading entities, entity sets, function imports, complex types, associations and domains that I don’t need/want, to keep the model as simple as possible and eliminate the errors.
- Class /IWBEP/CL_OCI_MP
The first problem is that, when you read the metadata, you get two Schemas. SFODataSet and SFOData. The code tries to read the entities from first schema, and entity sets from second schema, but I found out that it should be the other way around, so everywhere where I saw this
in the methods below, I changed the index to 2.
Second change was to filter out all I did not want. That was done on DEFINE. I created a range of the entities I was interested on and used that to delete everything that did not reference these entities. Also commented create_oci_funcimports() as I was not interested in it at all:
Overview of changes: Overwrite exists on the following methods:
- DEFINE
- CREATE_OCI_FUNCIMPORTS
- PREPARE_ENTITIES
- PREPARE_ENTITY_PROPERTIES
- PREPARE_COMPLEX_PROPERTIES
- PREPARE_ASSOCIATIONS
- PREPARE_FUNCTIONIMPORTS
- PREPARE_COMPLEX_TYPE_PROPS
And that’s it! It turned out to be longer that I expected, but I really hope this help someone and also I hope to get some traction here and get some thoughts on how the solution was implemented.
Thank you all!
Vicente,
How is authentication handled here? Are you handling it in your HTTP connection details? Is it possible to use SSO? Great blog! Thanks.
Hi Kunal,
I'm just using basic authentication for now. Apparently you can use SSO, that would be the option below basic authentication on the Logon&Security tab of the RFC Destination details:
SAP Logon Ticket
When you activate this option, an attempt is made to create and send the SAP logon ticket for the current session, for a logon to the target system. Before you can create this ticket, the environment must be configured appropriately (for example, the profile parameter login/create_sso2_ticket must be set to 2).
I do have SSO configured between SAP and SFSF but I needed the API to be called as a power user as regular users would not have all the access they need.
Please let me know if that works for you!
Kr,
Hello Vicente,
Another question I had was why do we have to build the service structure in the ABAP system? If the odata/v2 api is already exposed is it not possible to just call the services through the RFC registered in the Front End Server? It almost sounds like we have to wrap around the odata entities by recreating the same entities in the ABAP system. Is that correct or can we simply call the SFAPI from within a SAPUI5 application deployed on the Front End Server?
Regards,
Kunal.
Kunal,
You can just call the OData API from your controller in the frontend (this is, directly to SFSF without going through SAP) if that is what you want to do and your architecture allows it. In my case this was not possible. My users cant directly log in to SFSF, they need to go through SSO, so I cannot call the SFSF api server because I don't have credentials to pass to that call. Even if that wasn't a problem, then you'd have to think how to authenticate against the API server using that user's credentials without creating a security risk, for example, by having the user's id and password in the controller of your application. You could use oAuth, but then you would be exposing your SFSF client's internal key... Then you have Cross-Origin Resource Sharing issues that you'd need to think about, since your UI5 application and your SFSF instance don't have the same host name... I'm sure there is a good way to solve all these issues, I just couldn't figure it out.
Then there are other inconveniences, for example, you may want to call SFSF API but then tweak the results based on other data from SAP, in which case you'd have to do first 1 call to SFSF, 1 call to SAP and then curate your results in the frontend, potentially exposing too much information that didn't really need to be there.
All in all, you bring up a good point. You don't really need to do all the config to be able to use the RFC to make the call. So you can just have your own code where you instantiate an http client using that RFC and manually trigger get or post calls against the API server. This simplifies a lot the configuration, but then it makes it a bit more complicated to use the data back. The idea of the "wrap" is that you can use this to query SFSF just like if it was an SAP oData service so that you can get your data back in a nice format without having to deal with that yourself.
I do this on another project, for which I built an API caller around the SAP collaboration framework (CLB2_MAIN package). That then authenticates for me every time I make a call, and SAP keeps track of the people using the api, the token expiration handling, security and all that good stuff.
Â
Sorry for the mega post, I hope this helps you.
Kr,
Vicente,
Thanks for your response, it was really helpful. I am stuck in the same predicament that you have described in your first paragraph and all the issues you listed above is exactly what I'm facing! We of course cannot use the client_id/secret in the controller due to security risks. It seems that "wrapping" all the required SFAPI services may be the only option here as you also found! Going through your blog, when I get to the step to Redfine->Odata Service (GW) we get the error "service does not contain odata artifact". We added in a dummy Entity and EntitySet, but we are never able to pull in the metadata from the actual SFAPI through this method. The RFC test connection is successful and we see the response object over there, but it doesn't work as expected in the service builder.
Thanks
Kunal.
Sorry Kunal I missed your reply.
I just realized, throughout your comments you mention SFAPI in a few places. Please note this only works for an oData API, not SFAPI. SFAPI is SOAP and this tutorial will not help you with it. Redefine->Odata Service (GW) is looking for a $metadata file that contain the description of the oData service. SFAPI have a wsdl file, so not the same approach.
If you want to consume a web service from SAP here are this and this post that might get you on the right track.
Cheers.
Hi Vicente,
Sorry for the terminology confusion. We are using the odata v2 api. When we do the redefine part in the gateway service builder we get the message No odata artifacts exist. In our SM59 RFC we can see the odata metadata in the response body correctly. I am still stuck!
Thanks.
If you are on transaction SEGW, expand your service, and go to folder "service maintenance", right click on GATEWAYSERVER and click on maintain, the window that opens will show the system alias your service is connected to, on the bottom right side. Can you make a screenshot of that and paste it here. Just want o make sure your system alias is not LOCAL.