logo

Are you need IT Support Engineer? Free Consultant

SAP Job Scheduling Service: πŸ”§ Generate Your Own C…

  • By sujay
  • 04/05/2026
  • 6 Views

In ourΒ previous blog post, we explored how the SAP Job Scheduling Service REST API is now available on the Business Accelerator Hub and learned how to test it interactively. Today, we're taking it to the next level:Β generating your own type-safe client libraryΒ from the OpenAPI specification!

Whether you're building with Node.js, Java, Python, Go, or any other language, you can generate a custom client that gives you:

  • βœ…Β Type safety and autocompletionΒ in your IDE
  • βœ…Β Reduced boilerplate codeΒ – no manual HTTP requests
  • βœ…Β Automatic authentication handlingΒ (OAuth 2.0)
  • βœ…Β Easy maintenanceΒ – regenerate when the API changes
  • βœ…Β Language-specific idiomsΒ – feels native to your stack

Let's dive in!


πŸ€” Why Generate a Client Library?

Before we jump into the “how,” let's understand the “why.”

Manual REST Calls vs. Generated Client

Manual ApproachΒ (the old way):

// Manual REST call - verbose and error-prone
const axios = require('axios');

// Get OAuth token
const tokenResponse = await axios.post('https://my-subdomain.authentication.eu10.hana.ondemand.com/oauth/token',
  'grant_type=client_credentials',
  {
    auth: { username: clientId, password: clientSecret },
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  }
);
const token = tokenResponse.data.access_token;

// Make API call
const response = await axios.get('https://jobscheduler-rest.cfapps.eu10.hana.ondemand.com/scheduler/jobs', {
  headers: { 'Authorization': `Bearer ${token}` }
});

// Parse response manually
const jobs = response.data.results;

Generated ClientΒ (the new way):

// Generated client - clean and type-safe
const client = new JobSchedulerClient(config);

// Authentication handled automatically
const jobs = await client.jobs.list();

Β 

Benefits of Generated Clients

🎯 Type Safety

  • IDE autocompletion for all methods and properties
  • Catch errors at compile time, not runtime
  • IntelliSense shows parameter types and descriptions

πŸ“¦Β Less Code

  • Authentication handled automatically
  • Request/response serialization built-in
  • Error handling standardized

πŸ”„Β Easy Updates

  • API changes? Regenerate the client
  • Breaking changes are immediately visible
  • Always in sync with the latest API version

🌍 Language Native

  • JavaScript clients use Promises
  • Java clients use standard HTTP clients
  • Python clients follow PEP conventions
  • Go clients use idiomatic patterns

πŸ“‹ Prerequisites

Before we begin, make sure you have:

  • βœ…Β Node.jsΒ installed (v18 or higher) –Β Download here
  • βœ…Β npmΒ orΒ yarnΒ package manager
  • βœ…Β Job Scheduler service instanceΒ on SAP BTP with a service key
  • βœ…Β OpenAPI Generator CLIΒ (we'll install this together)
  • βœ… Basic familiarity with JavaScript and REST APIs

For the working example, you'll need:

  • A running application that can be invoked by the Job Scheduler
  • Or use a public webhook testing service likeΒ webhook.site

πŸ“₯ Step 1: Get the OpenAPI Specification

First, we need the OpenAPI specification file that describes the Job Scheduler REST API.

Option A: Download from Business Accelerator Hub

  1. Go toΒ SAP Business Accelerator Hub
  2. Search for “Job Scheduling Service”
  3. In the section API Resources
  4. Look forΒ “API Specification”Β tab
  5. Download theΒ OpenAPI JSONΒ orΒ OpenAPI YAMLΒ file

Β 

Screenshot-Download-Openapi.png


βš™οΈ Step 2: Install OpenAPI Generator

We'll useΒ OpenAPI GeneratorΒ – a powerful tool that supports 50+ languages and frameworks.

Install via npm (Recommended)

npm install @openapitools/openapi-generator-cli -g

Verify installation:

openapi-generator-cli version

You should see output like:

Generator-Version-Output.png


🎨 Step 3: Generate a Node.js Client

Let's generate a JavaScript/Node.js client library from the OpenAPI specification.

Create a Working Directory

mkdir jobscheduler-client-example
cd jobscheduler-client-example

Β 

Use the OpenAPI Spec

For this example, copy the OpenAPI spec file to your working directory.

Generate the Client

Run the generator:

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g javascript \
  -o ./generated-client \
  --additional-properties=projectName=jobscheduler-client,moduleName=JobSchedulerClient,usePromises=true

Parameter Explanation:

  • -i sap-btpjss-admin-v1.yamlΒ – Input OpenAPI specification file
  • -g javascriptΒ – Target language (JavaScript/Node.js)
  • -o ./generated-clientΒ – Output directory
  • --additional-propertiesΒ – Customizations:
    • projectName=jobscheduler-clientΒ – NPM package name
    • moduleName=JobSchedulerClientΒ – Main module name
    • usePromises=trueΒ – Use Promises instead of callbacks

You should see output like:

[main] INFO  o.o.codegen.DefaultGenerator - Generating with dryRun=false
[main] INFO  o.o.c.l.JavascriptClientCodegen - Generating JavaScript client...
[main] INFO  o.o.codegen.DefaultGenerator - Successfully generated code to ./generated-client
....
############################################################################################
# Thanks for using OpenAPI Generator.                                                      #
# We appreciate your support! Please consider donation to help us maintain this project.   #
# https://opencollective.com/openapi_generator/donate                                      #
############################################################################################

Β 

What Got Generated?

Β 

Generated-Client-Structure.png

The generator creates a complete, ready-to-use client library:

generated-client/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ ApiClient.js           # Main API client with authentication
β”‚   β”œβ”€β”€ index.js               # Module entry point
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ JobsApi.js         # Jobs endpoints
β”‚   β”‚   β”œβ”€β”€ SchedulesApi.js    # Schedules endpoints
β”‚   β”‚   └── RunLogsApi.js      # Run logs endpoints
β”‚   β”œβ”€β”€ model/
β”‚   β”‚   β”œβ”€β”€ CreateJobRequest.js    # Job creation model
β”‚   β”‚   β”œβ”€β”€ JobResponse.js         # Job details model
β”‚   β”‚   β”œβ”€β”€ .......js              # Other models
β”‚   β”‚   β”œβ”€β”€ ScheduleResponse.js    # Schedule model
β”‚   β”‚   └── RunLogResponse.js      # Run log model
β”œβ”€β”€ docs/                      # API documentation (Markdown)
β”œβ”€β”€ test/                      # Unit tests
β”œβ”€β”€ package.json               # NPM dependencies
└── README.md                  # Usage instructions

πŸ” Step 4: Set Up Authentication

The Job Scheduler API usesΒ OAuth 2.0 Client Credentials flow. Let's configure authentication.

Get Your Service Key

From your Job Scheduler service instance in BTP Cockpit:

  1. Navigate toΒ Service InstancesΒ β†’ Your Job Scheduler instance
  2. ClickΒ “Create Service Key”Β (or use existing)
  3. Copy the service key JSON

Example service key:

{
  "url": "https://jobscheduler-rest.cfapps.eu10.hana.ondemand.com",
  "uaa": {
    "url": "https://my-subdomain.authentication.eu10.hana.ondemand.com",
    "clientid": "sb-clone-jobscheduler-service!b1234",
    "clientsecret": "your-secret-here"
  }
}

Install the Generated Client

Navigate to the generated client and install dependencies:

cd generated-client
npm install
cd ..

Create Your Application

Create a new package.json in your working directory (not inside generated-client😞

Install required dependencies:

YourΒ package.jsonΒ should look like:

{
  "name": "jobscheduler-client-example",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node example.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "axios": "^1.13.6",
    "qs": "^6.15.0"
  },
  "type": "module"
}

Note: SetΒ "type": "module"Β to use ES6 module syntax.Β Note: AddΒ "start": "node example.js"Β to your scripts for easy execution.


πŸ’» Step 5: Use the Generated Client – Practical Examples

Now let's write code that uses the generated client! We'll create a complete working example.

Create Configuration File

First, create aΒ config.jsonΒ file with your service key details:

{
  "jobScheduler": {
    "url": "https://jobscheduler-rest.cfapps.eu10.hana.ondemand.com",
    "uaa": {
      "url": "https://my-subdomain.authentication.eu10.hana.ondemand.com",
      "clientid": "sb-clone-jobscheduler-service!b1234",
      "clientsecret": "your-secret-here"
    }
  }
}

⚠️ Security Note: Never commit this file to version control! Add it to .gitignore.

Create Helper for OAuth Authentication

CreateΒ auth-helper.jsΒ to handle OAuth token retrieval:

import axios from 'axios';
import qs from 'qs';

/**
 * Get OAuth 2.0 access token using client credentials flow
 */
export async function getOAuthToken(uaaConfig) {
  const tokenUrl = `${uaaConfig.url}/oauth/token`;

  const response = await axios.post(
    tokenUrl,
    qs.stringify({ grant_type: 'client_credentials' }),
    {
      auth: {
        username: uaaConfig.clientid,
        password: uaaConfig.clientsecret
      },
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
  );

  return response.data.access_token;
}

Example 1: Initialize Client and List Jobs

CreateΒ example.js:

import fs from 'fs';
import { getOAuthToken } from './auth-helper.js';

// Import the generated client
// Note: Adjust the path based on where you generated the client
import JobSchedulerClient from './generated-client/dist/index.js';

/**
 * Initialize the Job Scheduler client with OAuth authentication
 */
async function initializeClient() {
  // Load configuration
  const config = JSON.parse(fs.readFileSync('./config.json', 'utf-8'));

  // Get OAuth token
  console.log('πŸ” Authenticating with OAuth 2.0...');
  const accessToken = await getOAuthToken(config.jobScheduler.uaa);
  console.log('βœ… Authentication successful\n');

  // Configure the API client
  const apiClient = new JobSchedulerClient.ApiClient();
  apiClient.basePath = config.jobScheduler.url + '/scheduler';
  apiClient.authentications['OAuth2ClientCredentials'].accessToken = accessToken;

  return {
    apiClient,
    jobsApi: new JobSchedulerClient.JobsApi(apiClient),
    // We'll add more APIs as we need them
  };
}

/**
 * Example 1: List all jobs
 */
async function listJobs(jobsApi) {
  console.log('πŸ“‹ Fetching all jobs...');

  try {
    // Use WithHttpInfo variant to access the raw JSON, since the list endpoint
    // returns `jobId` per item but the generated model only maps `_id`
    const { response } = await jobsApi.getAllJobsWithHttpInfo();
    const data = response.body;

    console.log(`βœ… Found ${data.total} job(s):\n`);

    data.results.forEach((job, index) => {
      console.log(`${index + 1}. ${job.name}`);
      console.log(`   ID: ${job.jobId ?? job._id}`);
      console.log(`   Action: ${job.action}`);
      console.log(`   Method: ${job.httpMethod}`);
      console.log(`   Active: ${job.active}`);
      console.log(`   Created: ${job.createdAt}\n`);
    });

    return data.results;
  } catch (error) {
    console.error('❌ Error fetching jobs:', error.message);
    throw error;
  }
}

// Main execution
(async () => {
  try {
    const { jobsApi } = await initializeClient();

    // List all jobs
    const jobs = await listJobs(jobsApi);

  } catch (error) {
    console.error('❌ Fatal error:', error.message);
    process.exit(1);
  }
})();

Run it:

Expected output:

πŸ” Authenticating with OAuth 2.0...
βœ… Authentication successful

πŸ“‹ Fetching all jobs...
βœ… Found 2 job(s):

1. validateSalesOrder
   ID: 12345
   Action: https://my-app.cfapps.eu10.hana.ondemand.com/orders/validate
   Method: POST
   Active: true
   Created: 2026-02-15T10:30:00Z

2. dailyReport
   ID: 12346
   Action: https://my-app.cfapps.eu10.hana.ondemand.com/reports/daily
   Method: GET
   Active: true
   Created: 2026-02-10T08:00:00Z

Example 2: Create a New Job

Add this function toΒ example.js:

/**
 * Example 2: Create a new job with a schedule
 */
async function createJob(jobsApi) {
  console.log('πŸ”¨ Creating a new job...');

  const jobRequest = {
    name: 'generatedClientExample',
    description: 'Job created using the generated client library',
    action: 'https://webhook.site/your-unique-id',  // Use webhook.site for testing
    httpMethod: 'POST',
    active: true,
    schedules: [
      {
        description: 'Run every 5 minutes',
        cron: '*/5 * * * *',  // Every 5 minutes
        active: true
      }
    ]
  };

  try {
    const createdJob = await jobsApi.createJob(jobRequest);

    console.log('βœ… Job created successfully!');
    console.log(`   Job ID: ${createdJob._id}`);
    console.log(`   Job Name: ${createdJob.name}`);
    console.log(`   Schedules: ${createdJob.schedules.length}\n`);

    return createdJob;
  } catch (error) {
    console.error('❌ Error creating job:', error.message);
    throw error;
  }
}

Update the main execution:

// Main execution
(async () => {
  try {
    const { jobsApi } = await initializeClient();

    // Create a job
    const newJob = await createJob(jobsApi);

    // List all jobs (including the new one)
    await listJobs(jobsApi);

  } catch (error) {
    console.error('❌ Fatal error:', error.message);
    process.exit(1);
  }
})();

Run it again:

Expected output will include the newly created job:

npm start

> jobscheduler-client-example@1.0.0 start
> node example.js

πŸ” Authenticating with OAuth 2.0...
βœ… Authentication successful

πŸ”¨ Creating a new job...
βœ… Job created successfully!
   Job ID: 3833017
   Job Name: generatedClientExample
   Schedules: 1

πŸ“‹ Fetching all jobs...
βœ… Found 6 job(s):

1. generatedClientExample
   ID: 3833017
   Action: https://sap.com
   Method: POST
   Active: true
   Created: 2026-03-05 14:26:56

Example 3: Get Job Details

/**
 * Example 3: Get details of a specific job
 */
async function getJobDetails(jobsApi, jobId) {
  console.log(`πŸ” Fetching details for job ${jobId}...`);

  try {
    const job = await jobsApi.getJobById(jobId);

    console.log('βœ… Job details:');
    console.log(`   Name: ${job.name}`);
    console.log(`   Description: ${job.description}`);
    console.log(`   Action: ${job.action}`);
    console.log(`   HTTP Method: ${job.httpMethod}`);
    console.log(`   Active: ${job.active}`);
    console.log(`   Schedules: ${job.schedules ? job.schedules.length : 0}\n`);

    if (job.schedules && job.schedules.length > 0) {
      console.log('   Schedule Details:');
      job.schedules.forEach((schedule, index) => {
        console.log(`   ${index + 1}. ${schedule.description}`);
        console.log(`      Cron: ${schedule.cron || 'N/A'}`);
        console.log(`      Active: ${schedule.active}\n`);
      });
    }

    return job;
  } catch (error) {
    console.error('❌ Error fetching job details:', error.message);
    throw error;
  }
}

Mofidy Main to use the new function, you can try it out with theΒ jobIdΒ of the job you just created or any existing job:

// Main execution
(async () => {
  try {
    const { jobsApi } = await initializeClient();

	const job = await getJobDetails(jobsApi, "3833032");

  } catch (error) {
    const status = error.status || error.response?.status;
    const body = error.response?.text || error.response?.body || error.message || error;
    console.error(`❌ Fatal error [${status}]:`, body);
    process.exit(1);
  }
})();

Result of running the main execution with this function:

npm start

> jobscheduler-client-example@1.0.0 start
> node example.js

πŸ” Authenticating with OAuth 2.0...
βœ… Authentication successful

πŸ” Fetching details for job 3833032...
βœ… Job details:
   Name: generatedClientExample
   Description: Job created using the generated client library
   Action: https://sap.com
   HTTP Method: POST
   Active: true
   Schedules: 0

Β 

You can use it for all of the APIs following the same pattern.


πŸ”₯ Complete Working Example

Here's a completeΒ example.jsΒ that puts it all together:

import fs from 'fs';
import { getOAuthToken } from './auth-helper.js';
import JobSchedulerClient from './generated-client/dist/index.js';

/**
 * Initialize the Job Scheduler client with OAuth authentication
 */
async function initializeClient() {
  const config = JSON.parse(fs.readFileSync('./config.json', 'utf-8'));

  console.log('πŸ” Authenticating with OAuth 2.0...');
  const accessToken = await getOAuthToken(config.jobScheduler.uaa);
  console.log('βœ… Authentication successful\n');

  const apiClient = new JobSchedulerClient.ApiClient();
  apiClient.basePath = config.jobScheduler.url + '/scheduler';
  apiClient.authentications['OAuth2ClientCredentials'].accessToken = accessToken;

  return new JobSchedulerClient.JobsApi(apiClient);
}

/**
 * Complete workflow demonstration
 */
async function runCompleteExample(jobsApi) {
  try {
    // 1. List existing jobs
    console.log('━━━ Step 1: List Existing Jobs ━━━');
    const existingJobs = await jobsApi.getAllJobs();
    console.log(`Found ${existingJobs.total} existing job(s)\n`);

    // 2. Create a new job
    console.log('━━━ Step 2: Create New Job ━━━');
    const newJob = await jobsApi.createJob({
      name: 'exampleJob_' + Date.now(),
      description: 'Demo job from generated client',
      action: 'https://webhook.site/your-unique-id',
      httpMethod: 'POST',
      active: true,
      schedules: [
        {
          description: 'Run once in 2 minutes',
          time: new Date(Date.now() + 120000).toISOString(),
          active: true
        }
      ]
    });
    console.log(`βœ… Created job: ${newJob.name} (ID: ${newJob._id})\n`);

    // 3. Get job details
    console.log('━━━ Step 3: Get Job Details ━━━');
    const jobDetails = await jobsApi.getJobById(newJob._id, {displaySchedules: true});
    console.log(`Job: ${jobDetails.name}`);
    console.log(`Schedules: ${jobDetails.schedules.length}\n`);

    // 4. Update the job
    console.log('━━━ Step 4: Update Job ━━━');
    await jobsApi.updateJobById(newJob._id, {
      description: 'Updated description from generated client'
    });
    console.log('βœ… Job updated\n');

    // 5. Wait a bit and check run logs (optional)
    console.log('━━━ Step 5: Check Run Logs ━━━');
    console.log('(Run logs will appear after the schedule executes)\n');

    // 6. Clean up - delete the job
    // console.log('━━━ Step 6: Cleanup ━━━');
    // await jobsApi.deleteJob(newJob._id);
    // console.log('βœ… Job deleted\n');

    console.log('πŸŽ‰ Complete example finished successfully!');

  } catch (error) {
    console.error('❌ Error in workflow:', error.message);
    if (error.response) {
      console.error('Response:', error.response.data);
    }
    throw error;
  }
}

// Main execution
(async () => {
  try {
    const jobsApi = await initializeClient();
    await runCompleteExample(jobsApi);
  } catch (error) {
    console.error('❌ Fatal error:', error.message);
    process.exit(1);
  }
})();

Β 

Run the complete example:


🚨 Error Handling

The generated client throws exceptions for error responses. Here's how to handle them properly:

async function createJobWithErrorHandling(jobsApi, jobRequest) {
  try {
    const job = await jobsApi.createJob(jobRequest);
    return job;
  } catch (error) {
    // Check if it's an API error with response
    if (error.response) {
      const status = error.response.status;
      const data = error.response.data;

      switch (status) {
        case 400:
          console.error('❌ Bad Request:', data.message);
          console.error('   Check your job request format');
          break;
        case 401:
          console.error('❌ Unauthorized:', data.message);
          console.error('   Your OAuth token may have expired');
          break;
        case 409:
          console.error('❌ Conflict:', data.message);
          console.error('   A job with this name already exists');
          break;
        case 429:
          console.error('❌ Rate Limited:', data.message);
          console.error('   Retry after:', error.response.headers['retry-after']);
          break;
        default:
          console.error(`❌ API Error (${status}):`, data.message);
      }
    } else {
      // Network error or other issue
      console.error('❌ Network Error:', error.message);
    }

    throw error;
  }
}

Β 

Common Error Scenarios

Error Code Meaning Solution

400 Bad Request Invalid request format Check request body schema
401 Unauthorized Authentication failed Refresh OAuth token
404 Not Found Job/schedule doesn't exist Verify ID is correct
409 Conflict Resource already exists Use a different name
429 Too Many Requests Rate limit exceeded Wait and retry
503 Service Unavailable Service overloaded Retry with backoff

🌍 Other Languages

The OpenAPI Generator supports 50+ languages! Here's how to generate clients for other popular languages:

TypeScript

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g typescript-axios \
  -o ./typescript-client \
  --additional-properties=npmName=jobscheduler-client,supportsES6=true

Β 

Benefits: Full type safety, perfect for Angular/React/Vue projects

Java

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g java \
  -o ./java-client \
  --additional-properties=library=okhttp-gson,artifactId=jobscheduler-client,groupId=com.sap

Β 

Benefits: Type-safe, integrates with Spring Boot, Maven/Gradle support

Python

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g python \
  -o ./python-client \
  --additional-properties=packageName=jobscheduler_client

Β 

Benefits: Clean Pythonic API, works with Django/Flask, supports async

Go

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g go \
  -o ./go-client \
  --additional-properties=packageName=jobscheduler

Β 

Benefits: High performance, native concurrency, small binaries

C# / .NET

openapi-generator-cli generate \
  -i sap-btpjss-admin-v1.yaml \
  -g csharp \
  -o ./csharp-client \
  --additional-properties=packageName=JobScheduler.Client

Β 

Benefits: Full .NET integration, async/await support, NuGet packaging


πŸ†š Generated Client vs. Official SAP Clients

You might be wondering: “Should I use the generated client or the officialΒ @sap/jobs-client?”

Generally use the Generated Client – the OfficialΒ @sap/jobs-clientΒ will be deprecated. (we may publish a new official client in the future, but for now the generated client is the way to go!)

Advantages of Generated Clients

βœ…Β Latest API featuresΒ – Generated from the current OpenAPI spec βœ…Β Custom languagesΒ – Need Python, Go, or another language SAP doesn't provide βœ…Β Full API coverageΒ – Access every endpoint, even newly released ones βœ…Β Type safetyΒ – Especially important for TypeScript projects βœ…Β CustomizationΒ – Modify the generated code for specific needs


πŸ’‘ Tips and Best Practices

1. Keep the OpenAPI Spec Updated

When the Job Scheduler API changes:

# Download the latest spec from Business Accelerator Hub

# Regenerate the client
openapi-generator-cli generate -i sap-btpjss-admin-v1.yaml -g javascript -o ./generated-client

# Test your application
npm test

Β 

2. Version Your Generated Client

Add the generated client to your repository or publish it as a private NPM package:

{
  "name": "myapp",
  "dependencies": {
    "jobscheduler-client": "file:./generated-client"
  }
}

Β 

Or publish to a private registry:

cd generated-client
npm publish --registry=https://your-private-registry.com

Β 

3. Create a Wrapper Layer

Don't expose the generated client directly. Create a wrapper:

// jobscheduler-service.js
import JobSchedulerClient from './generated-client/dist/index.js';
import { getOAuthToken } from './auth-helper.js';

class JobSchedulerService {
  constructor(config) {
    this.config = config;
    this.client = null;
  }

  async initialize() {
    const token = await getOAuthToken(this.config.uaa);

    const apiClient = new JobSchedulerClient.ApiClient();
    apiClient.basePath = this.config.url + '/scheduler';
    apiClient.authentications['OAuth2ClientCredentials'].accessToken = token;

    this.client = new JobSchedulerClient.JobsApi(apiClient);
  }

  async createDailyJob(name, action) {
    return this.client.createJob({
      name,
      action,
      httpMethod: 'POST',
      active: true,
      schedules: [{
        description: 'Daily at 8 AM',
        cron: '0 8 * * *',
        active: true
      }]
    });
  }

  // Add more convenience methods...
}

export default JobSchedulerService;

Β 

Benefits:

  • Hide generated client complexity
  • Add business logic
  • Easier to swap implementations
  • Centralized error handling

4. Testing Strategies

Test your generated client integration:

// tests/jobscheduler.test.js
import { expect } from 'chai';
import JobSchedulerService from '../jobscheduler-service.js';

describe('Job Scheduler Integration', () => {
  let service;

  before(async () => {
    service = new JobSchedulerService(config);
    await service.initialize();
  });

  it('should create a job', async () => {
    const job = await service.createDailyJob('testJob', 'https://example.com');
    expect(job).to.have.property('_id');
    expect(job.name).to.equal('testJob');
  });

  it('should list jobs', async () => {
    const jobs = await service.listJobs();
    expect(jobs).to.be.an('array');
  });
});

Β 

5. Handle Token Refresh

OAuth tokens expire. Implement automatic refresh:

class TokenManager {
  constructor(uaaConfig) {
    this.uaaConfig = uaaConfig;
    this.token = null;
    this.expiresAt = null;
  }

  async getToken() {
    // Check if token is expired or will expire in next 5 minutes
    if (!this.token || Date.now() >= this.expiresAt - 300000) {
      await this.refreshToken();
    }
    return this.token;
  }

  async refreshToken() {
    const response = await getOAuthToken(this.uaaConfig);
    this.token = response.access_token;
    // Tokens typically expire in 12 hours
    this.expiresAt = Date.now() + (response.expires_in * 1000);
  }
}

Β 

6. Use Environment Variables

Never hardcode credentials:

// .env file (add to .gitignore!)
JOB_SCHEDULER_URL=https://jobscheduler-rest.cfapps.eu10.hana.ondemand.com
UAA_URL=https://my-subdomain.authentication.eu10.hana.ondemand.com
UAA_CLIENT_ID=sb-clone-jobscheduler-service!b1234
UAA_CLIENT_SECRET=your-secret-here

// In your code:
import dotenv from 'dotenv';
dotenv.config();

const config = {
  url: process.env.JOB_SCHEDULER_URL,
  uaa: {
    url: process.env.UAA_URL,
    clientid: process.env.UAA_CLIENT_ID,
    clientsecret: process.env.UAA_CLIENT_SECRET
  }
};

Β 


πŸ“¦ Project Files Reference

For your convenience, here's the complete file structure for the working example:

jobscheduler-client-example/
β”œβ”€β”€ package.json                 # Project dependencies
β”œβ”€β”€ .gitignore                   # Ignore node_modules, config, generated client
β”œβ”€β”€ config.json                  # Service key configuration (DO NOT COMMIT)
β”œβ”€β”€ example.js                   # Complete working example
β”œβ”€β”€ sap-btpjss-admin-v1.yaml     # OpenAPI specification
└── generated-client/            # Generated client library (can be gitignored)
    β”œβ”€β”€ src/
    β”œβ”€β”€ docs/
    β”œβ”€β”€ package.json
    └── README.md

.gitignore

# Dependencies
node_modules/
package-lock.json

# Configuration (contains secrets!)
config.json
.env

# Generated client (optional - you can commit this)
# generated-client/

# IDE
.vscode/
.idea/

# OS
.DS_Store

πŸ“š Additional Resources

OpenAPI Generator

Job Scheduler Resources

Related Blog Posts


πŸŽ‰ Summary

Congratulations! You've learned how to:

βœ…Β DownloadΒ the OpenAPI specification from Business Accelerator Hub βœ…Β InstallΒ OpenAPI Generator βœ…Β GenerateΒ a type-safe client library in Node.js βœ…Β ConfigureΒ OAuth 2.0 authentication βœ…Β UseΒ the generated client to manage jobs, schedules, and run logs βœ…Β HandleΒ errors and edge cases βœ…Β GenerateΒ clients for other languages (Java, Python, Go, etc.)

Key Takeaways

🎯 Generated clients save timeΒ – Less boilerplate, more productivity πŸ›‘οΈΒ Type safety prevents bugsΒ – Catch errors at compile time πŸ”„Β Easy to updateΒ – Regenerate when the API changes 🌍 Language flexibilityΒ – Use any language you want πŸ’ΌΒ Production-readyΒ – With proper error handling and token management

What's Next?

Now that you have a working client, you can:

  • Integrate job scheduling into your SAP BTP applications
  • Automate job management with CI/CD pipelines
  • Build monitoring dashboards using run log APIs
  • Create job templates for common patterns
  • Contribute to the SAP community with your own examples!

πŸ’¬ Questions or Feedback?

Have questions about generating clients or using the Job Scheduler API?


Happy Coding!Β πŸš€

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *