Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to run background jobs which require Principal Propagation to SAP Backend? #58

Open
codeyogi911 opened this issue Sep 9, 2024 · 10 comments
Assignees
Labels
question Further information is requested

Comments

@codeyogi911
Copy link

I have a use case where the background job needs to access the backend system via destinations in a multi tenant app. How can i achieve this? Basically i want to run a background job with a technical user

@alperdedeoglu alperdedeoglu self-assigned this Sep 9, 2024
@alperdedeoglu alperdedeoglu added the question Further information is requested label Sep 9, 2024
@alperdedeoglu
Copy link
Contributor

Hi @codeyogi911,

cds.spawn should help you with your use case as described here.

cds.spawn ({ tenant:'t0', every: 1000 /* ms */ }, async (tx) => {
  const mails = await SELECT.from('Outbox')
  await MailServer.send(mails)
  await DELETE.from('Outbox').where (`ID in ${mails.map(m => m.ID)}`)
})

@codeyogi911
Copy link
Author

@alperdedeoglu I am aware about how to use cds.spawn, what I want to find out is how to run with a jwt in the background?

@alperdedeoglu
Copy link
Contributor

alperdedeoglu commented Sep 9, 2024

@codeyogi911 What kind of SAP Backend are we talking about? Can you explain the exact use case you have?
If that is S/4HANA Private Cloud or on-premise, you need to get the token for destination service on behalf of your tenant.
You can do this each time when your job gets executed. I am having hard times understanding what is the actual problem you are having.

@codeyogi911
Copy link
Author

I have an SAP Onprem system connected to our multi-tenant app. We are running in the background a Auto Sales Order job. When connecting to the backend API it fails because there is no jwt. Is there a way to configure and use technical user for each tenant?
What i did currently is get the jwt using password_grant (which is not correct) and created a cds.user using this token and @sap/xssec. I am then using this user in the cds.spawn.
I feel like its a work around and there should be a way to configure and use background users.

@alperdedeoglu
Copy link
Contributor

alperdedeoglu commented Sep 9, 2024

Where exactly do you get your JWT from? Do you mean the XSUAA? By Backend API do you mean CAP API or S/4 API?

@codeyogi911
Copy link
Author

Right now as a workaround i get the jwt from the bound XSUAA using a password_grant. Backend API means S4 API using Destinations and Cloud Connector.

@alperdedeoglu
Copy link
Contributor

Right now as a workaround i get the jwt from the bound XSUAA using a password_grant. Backend API means S4 API using Destinations and Cloud Connector.

And are you using Cloud SDK or something to connect to the backend? Or have you defined a destination? How do you call the on-premise backend? Which library do you use?

@codeyogi911
Copy link
Author

I have my function to create sales order defined like:

async function createSalesOrder(orderData) {
  LOGGER.info("Creating sales order: ", cds.context.user)
  const salesSrv = await cds.connect.to("API_SALES_ORDER_SRV", {
    destinationOptions: {
      jwt: cds.context.user.tokenInfo.getTokenValue(),
    }
  })
  const result = await salesSrv.create("A_SalesOrder").entries(orderData)
  LOGGER.info("Sales order created", result.SalesOrder)
  return result
}

earlier i had it defined like:

async function createSalesOrder(orderData) {
  let salesSrv
  if (cds.context.isPrivilegedSpawn) {
    const subDomain = await getTenantSubdomain(cds.context.tenant)
    LOGGER.info("Subdomain for tenant", subDomain)
    LOGGER.info("Creating sales order for tenant using basic authentication", cds.context.tenant)
    salesSrv = await cds.connect.to("API_SALES_ORDER_SRV", {
      credentials: {
        destination: "onpremise-basic",
        path: "/sap/opu/odata/sap/API_SALES_ORDER_SRV",
      },
      destinationOptions: {
        selectionStrategy: "alwaysSubscriber",
        iss: `https://${subDomain}.authentication.eu10.hana.ondemand.com/oauth/token`,
        useCache: true,
      },
    })
  } else {
    salesSrv = await cds.connect.to("API_SALES_ORDER_SRV")
  }
  const result = await salesSrv.create("A_SalesOrder").entries(orderData)
  LOGGER.info("Sales order created", result.SalesOrder)
  return result
}

so this stopped working after the upgrading to CDS8, it somehow now gives me 401 when using onpremise-basic and even if i try in the foreground then also it gives me 401, even though it should use the onpremise in foreground.
Somehow the destination is not getting cached even after setting

"API_SALES_ORDER_SRV": {
        "kind": "odata-v2",
        "model": "srv/external/API_SALES_ORDER_SRV",
        "csrf": true,
        "csrfInBatch": true,
        "[production]": {
          "credentials": {
            "destination": "onpremise",
            "path": "/sap/opu/odata/sap/API_SALES_ORDER_SRV"
          },
          "destinationOptions": {
            "selectionStrategy": "alwaysSubscriber",
            "useCache": false
          }
        }
      },

@alperdedeoglu
Copy link
Contributor

Ok got it, can you just give the jwt with retrieving a token via client_credentials and let me know how it goes?

@alperdedeoglu
Copy link
Contributor

Hi @codeyogi911, did that work for you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants