This project contains documentation and other resources related to IBM Cloud SDKs
Table of Contents
- Overview
- SDK Project Catalog
- Using the SDK
- Constructing service clients
- Authentication
- Passing parameters to operations
- Receiving operation responses
- Sending HTTP headers
- Transaction IDs
- Configuring the HTTP Client
- Configuring Request Timeouts
- Automatic retries
- Disabling SSL Verification - Discouraged
- Error Handling
- Logging
- Synchronous and asynchronous requests
- Questions
- Open source @ IBM
- License
IBM Cloud SDKs allow developers to programmatically interact with various IBM Cloud Services. SDKs are provided for the following languages:
- Go
- Java
- Node.js
- Python
The following table provides links to the various SDK projects associated with IBM Cloud Service categories:
This section provides general information on how to use the services contained in an SDK project. The programming examples below will illustrate how the various activities can be performed for each of the programming languages mentioned above using a mythical service named "Example Service" that is defined in example-service.yaml.
Each service client - the client-side view of a service - is implemented in its own class (Java, Node.js, Python) or struct (Go) within the corresponding SDK project. For example, the "Example Service" client is implemented as:
- Go: the
ExampleServiceV1
struct within theexampleservicev1
package - Java: the
ExampleService
class within thecom.ibm.cloud.mysdk.example_service.v1
package - Node.js: the
ExampleServiceV1
class in theexample-service/v1
module - Python: the
ExampleServiceV1
class within themysdk
module
The SDK allows you to construct the service client in one of two ways:
- Setting client options programmatically
- Using external configuration properties
Here's an example of how to construct an instance of the "Example Service" client while specifying various client options (authenticator, service endpoint URL, etc.) programmatically:
Go
import {
"github.com/IBM/go-sdk-core/v4/core"
"github.com/IBM/mysdk/exampleservicev1"
}
// Create an IAM authenticator.
authenticator := &core.IamAuthenticator{
ApiKey: "<iam-api-key>",
}
// Construct an "options" struct for creating the service client.
options := &exampleservicev1.ExampleServiceV1Options{
Authenticator: authenticator, // required
URL: "https://example-service.cloud.ibm.com/v1", // optional
}
// Construct the service client.
myService, err := exampleservicev1.NewExampleServiceV1(options)
if err != nil {
panic(err)
}
// Service operations can now be invoked using the "myService" variable.
Java - for Java core versions prior to 9.7.0 (deprecated)
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
import com.ibm.cloud.sdk.core.security.Authenticator;
import com.ibm.cloud.sdk.core.security.IamAuthenticator;
// Create an IAM authenticator.
Authenticator authenticator = new IamAuthenticator("<iam-api-key>");
// Construct the service client.
ExampleService myService = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
// Set our custom service URL (optional).
myService.setServiceUrl("https://example-service.cloud.ibm.com/v1");
// Service operations can now be called using the "myService" variable.
Java - for Java core version 9.7.0 and above
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
import com.ibm.cloud.sdk.core.security.Authenticator;
import com.ibm.cloud.sdk.core.security.IamAuthenticator;
// Create an IAM authenticator.
Authenticator authenticator = new IamAuthenticator.Builder()
.apikey("<iam-api-key>")
.build();
// Construct the service client.
ExampleService myService = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
// Set our custom service URL (optional).
myService.setServiceUrl("https://example-service.cloud.ibm.com/v1");
// Service operations can now be called using the "myService" variable.
Node.js
const ExampleServiceV1 = require('mysdk/example-service/v1');
const { IamAuthenticator } = require('mysdk/auth');
// Create an IAM authenticator.
const authenticator = new IamAuthenticator({
apikey: '<iam-api-key>',
});
// Construct the service client.
const myService = new ExampleServiceV1({
authenticator, // required
serviceUrl: 'https://example-service.cloud.ibm.com/v1', // optional
});
// Service operations can now be invoked using the "myService" variable.
Python
from mysdk import ExampleServiceV1
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
# Create an IAM authenticator.
authenticator = IAMAuthenticator('<iam-api-key>')
# Construct the service client.
my_service = ExampleServiceV1(authenticator=authenticator)
# Set our custom service URL (optional)
my_service.set_service_url('https://example-service.cloud.ibm.com/v1')
# Service operations can now be invoked using the "my_service" variable.
For a typical application deployed to the IBM Cloud, it might be convenient or necessary to avoid hard-coding certain service client options (IAM API Key, service URL, etc.) within the application. Instead, the SDK allows you to store these values in configuration properties external to your application.
First, define the configuration properties to be used by your application. These properties can be implemented as either:
- Exported environment variables
- Stored in a credentials file.
In the examples that follow, we'll use environment variables to implement our configuration properties.
Each property name is of the form: <service-name>_<property-key>
.
Here is an example of some configuration properties for the "Example Service" service:
export EXAMPLE_SERVICE_URL=https://example-service.cloud.ibm.com/v1
export EXAMPLE_SERVICE_AUTH_TYPE=iam
export EXAMPLE_SERVICE_APIKEY=<iam-api-key>
The service name "example_service" is the default service name for the "Example Service" client, so the SDK will (by default) look for properties that start with this prefix folded to upper case.
After you have defined the configuration properties for your application, you can construct an instance of the service client like the examples below:
Go
// Construct service client via config properties using default service name ("example_service")
myService, err := exampleservicev1.NewExampleServiceV1UsingExternalConfig(
&exampleservicev1.ExampleServiceV1Options{})
if err != nil {
panic(err)
}
The NewExampleServiceV1UsingExternalConfig
function will:
- construct an authenticator using the environment variables above (an IAM authenticator using
"<iam-api-key>"
as the api key). - initialize the service client to use service URL "https://example-service.cloud.ibm.com/v1".
Java
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
ExampleService myService = ExampleService.newInstance();
The ExampleService.newInstance()
method will:
- construct an authenticator using the environment variables above (an IAM authenticator using
"<iam-api-key>"
as the api key). - initialize the service client to use service URL "https://example-service.cloud.ibm.com/v1".
Node.js
const ExampleServiceV1 = require('mysdk/example-service/v1');
const myService = ExampleServiceV1.newInstance();
The ExampleServiceV1.newInstance()
method will:
- construct an authenticator using the environment variables above (an IAM authenticator using
"<iam-api-key>"
as the api key). - initialize the service client to use service URL "https://example-service.cloud.ibm.com/v1".
Python
from mysdk import ExampleServiceV1
my_service = ExampleServiceV1.new_instance()
The ExampleServiceV1.new_instance()
method will:
- construct an authenticator using the environment variables above (an IAM authenticator using
"<iam-api-key>"
as the api key). - initialize the service client to use service URL "https://example-service.cloud.ibm.com/v1".
Instead of exporting your configuration properties as environment variables, you can store the properties in a credentials file. Here is an example of a credentials file that contains the properties from the examples above:
# Contents of "mycredentials.env"
EXAMPLE_SERVICE_URL=https://example-service.cloud.ibm.com/v1
EXAMPLE_SERVICE_AUTH_TYPE=iam
EXAMPLE_SERVICE_APIKEY=iam-api-key
You would then provide the name of the credentials file via the IBM_CREDENTIALS_FILE
environment variable:
export IBM_CREDENTIALS_FILE=/myfolder/mycredentials.env
When the SDK needs to look for configuration properties, it will detect the IBM_CREDENTIALS_FILE
environment
variable, then load the properties from the specified file.
The URLs used to construct service clients - either programmatically, or via external configuration - are not validated. Take care that your URL is valid, ensuring at least the presence of a valid protocol and host.
Note that a user information component should not be present as it may interfere with the correct operation of the configured authenticators, for example by forcing the SDK's underlying HTTP client to add an additional authentication scheme.
The above examples provide a glimpse of two specific ways to provide external configuration to the SDK
(environment variables and credentials file specified via the IBM_CREDENTIALS_FILE
environment variable).
The complete configuration-loading process supported by the SDK is as follows:
- Look for a credentials file whose name is specified by the
IBM_CREDENTIALS_FILE
environment variable - Look for a credentials file at
<current-working-directory>/ibm-credentials.env
- Look for a credentials file at
<user-home-directory>/ibm-credentials.env
- Look for environment variables whose names start with
<upper-case-service-name>_
(e.g.EXAMPLE_SERVICE_
)
At each of the above steps, if one or more configuration properties are found for the specified service, those properties are then returned to the SDK and any subsequent steps are bypassed.
IBM Cloud Services use token-based Identity and Access Management (IAM) authentication.
With IAM authentication, you supply an API key to obtain an access token, and this access token is then included with each API request to provide the required authentication information. Access tokens are valid only for a limited amount of time and must be refreshed or reacquired.
To provide the proper authentication information to the SDK, you can use one of the following scenarios:
In this scenario, you construct an IAM authenticator instance, supplying your IAM api key programmatically.
The SDK's IAM authenticator will:
- Use your API key to obtain an access token from the IAM token service
- Ensure that the access token is valid
- Include the access token in each outgoing request
- Refresh or reacquire the access token when it expires
Go
import {
"github.com/IBM/go-sdk-core/v4/core"
"github.com/IBM/mysdk/exampleservicev1"
}
authenticator := &core.IamAuthenticator{
ApiKey: "<iam-api-key>",
}
options := &exampleservicev1.ExampleServiceV1Options{
Authenticator: authenticator,
}
myService, err := exampleservicev1.NewExampleServiceV1(options)
if err != nil {
panic(err)
}
Java - for Java core versions prior to 9.7.0 (deprecated)
import com.ibm.cloud.sdk.core.security.Authenticator;
import com.ibm.cloud.sdk.core.security.IamAuthenticator;
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
Authenticator authenticator = new IamAuthenticator("<iam-api-key>");
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
Java - for Java core version 9.7.0 and above
import com.ibm.cloud.sdk.core.security.Authenticator;
import com.ibm.cloud.sdk.core.security.IamAuthenticator;
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
Authenticator authenticator = new IamAuthenticator.Builder()
.apikey("<iam-api-key>")
.build();
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
Node.js
const ExampleServiceV1 = require('mysdk/example-service/v1');
const { IamAuthenticator } = require('mysdk/auth');
const authenticator = new IamAuthenticator({
apikey: '<iam-api-key>',
});
const myService = new ExampleServiceV1({
authenticator,
});
Python
from mysdk import ExampleServiceV1
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
authenticator = IAMAuthenticator('<iam-api-key>')
my_service = ExampleServiceV1(authenticator=authenticator)
In this scenario, you define the IAM api key in external configuration properties, and then use the SDK's authenticator factory to construct an IAM authenticator using the configured IAM api key. This allows you to avoid hard-coding your api key within your application code or having to concern yourself with loading it from configuration properties.
The SDK's authenticator factory will:
- Retrieve your external configuration to obtain your IAM api key
- Construct an IAM authenticator using the configured IAM api key
- The IAM authenticator will then behave exactly the same as the authenticator constructed in Scenario 1 above.
The programming examples in this section assume that external configuration properties like the following have been configured:
export EXAMPLE_SERVICE_AUTH_TYPE=iam
export EXAMPLE_SERVICE_APIKEY=<iam-api-key>
Go
import {
"github.com/IBM/go-sdk-core/v4/core"
"github.com/IBM/mysdk/exampleservicev1"
}
authenticator, err := core.GetAuthenticatorFromEnvironment(exampleservicev1.DefaultServiceName)
if err != nil {
panic(err)
}
options := &exampleservicev1.ExampleServiceV1Options{
Authenticator: authenticator,
}
myService, err := exampleservicev1.NewExampleServiceV1(options)
if err != nil {
panic(err)
}
Java
import com.ibm.cloud.sdk.core.security.Authenticator;
import com.ibm.cloud.sdk.core.security.ConfigBasedAuthenticatorFactory;
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
Authenticator authenticator = ConfigBasedAuthenticatorFactory.getAuthenticator(ExampleService.DEFAULT_SERVICE_NAME);
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
Node.js
const ExampleServiceV1 = require('mysdk/example-service/v1');
const { IamAuthenticator, getAuthenticatorFromEnvironment } = require('mysdk/auth');
const authenticator = getAuthenticatorFromEnvironment(ExampleServiceV1.DEFAULT_SERVICE_NAME);
const myService = new ExampleServiceV1({
authenticator,
});
Python
from mysdk import ExampleServiceV1
from ibm_cloud_sdk_core import get_authenticator_from_environment
authenticator = get_authenticator_from_environment(ExampleServiceV1.DEFAULT_SERVICE_NAME)
my_service = ExampleServiceV1(authenticator=authenticator)
In this scenario, you are responsible for obtaining the access token from the IAM token service and refreshing or reacquiring it as needed. Once you obtain the access token, construct a Bearer Token authenticator and supply the access token.
Note that you can also construct a Bearer Token authenticator using external configuration properties, similar to Scenario 2 above with the IAM authenticator, but it is less useful to use that approach with the Bearer Token authenticator because you must manage the access token yourself.
Go
import {
"github.com/IBM/go-sdk-core/v4/core"
"github.com/IBM/mysdk/exampleservicev1"
}
authenticator := &core.BearerTokenAuthenticator{
BearerToken: "<access-token>",
}
// Create the service options struct.
options := &exampleservicev1.ExampleServiceV1Options{
Authenticator: authenticator,
}
// Construct the service instance.
myservice, err := exampleservicev1.NewExampleServiceV1(options)
...
// Later when the access token expires, the application must acquire
// a new access token, then set it on the authenticator.
// Subsequent request invocations will include the new access token.
authenticator.BearerToken = "<new-access-token>"
Java
import com.ibm.cloud.sdk.core.security.BearerTokenAuthenticator;
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
Authenticator authenticator = new BearerTokenAuthenticator("<access-token>");
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
...
// Later when the access token expires, the application must acquire
// a new access token, then set it on the authenticator.
// Subsequent request invocations will include the new access token.
authenticator.setBearerToken("<new-access-token>")
Node.js
const ExampleServiceV1 = require('mysdk/example-service/v1');
const { BearerTokenAuthenticator } = require('mysdk/auth');
const authenticator = new BearerTokenAuthenticator({
bearerToken: '<access-token>',
});
const myService = new ExampleServiceV1({
authenticator,
});
...
// Later when the access token expires, the application must acquire
// a new access token, then set it on the authenticator.
// Subsequent request invocations will include the new access token.
authenticator.setBearerToken('<new-access-token>')
Python
from mysdk import ExampleServiceV1
from ibm_cloud_sdk_core.authenticators import BearerTokenAuthenticator
authenticator = BearerTokenAuthenticator('<access-token>')
my_service = ExampleServiceV1(authenticator=authenticator)
...
// Later when the access token expires, the application must acquire
// a new access token, then set it on the authenticator.
// Subsequent request invocations will include the new access token.
authenticator.set_bearer_token('<new-access-token>')
For more details about authentication, including the full set of authentication schemes supported by the SDK Core library for your language, see these links:
For certain languages (namely Go, Java and Node.js) a structure is defined for each operation as a container for the parameters associated with that operation.
The use of these parameter container structures (instead of listing each operation parameter within the argument list of the operations) allows for future expansion of the API (within certain guidelines) without impacting application compatibility.
The language-specific sections that follow provide additional information and examples regarding these parameter container structures:
Go
For Go, a struct is defined for each operation, where the name of the struct will be
<operation-name>Options
and it will contain a field for each operation parameter.
Here's an example of an options struct for the GetResource
operation:
// GetResourceOptions : The GetResource options.
type GetResourceOptions struct {
// The id of the resource to retrieve.
ResourceID *string `json:"resource_id" validate:"required"`
...
}
In this example, the GetResource
operation has one parameter - ResourceID
.
When invoking this operation, the application first creates an instance of the GetResourceOptions
struct, sets the parameter value within it, then passes the options struct instance to the operation.
Along with the "options" struct, a constructor function is also provided.
Here's an example:
options := myService.NewGetResourceOptions("resource-id-1")
result, detailedResponse, err := myService.GetResource(options)
Java
For Java, a class is defined for each operation, where the name of the class will be
<operation-name>Options
and it will contain a field for each operation parameter.
Here's an example of an options class for the getResource
operation:
/**
* The getResource options.
*/
public class GetResourceOptions extends GenericModel {
protected String resourceId;
...
}
In this example, the getResource
operation has one parameter - resourceId
.
When invoking this operation, the application first creates an instance of the GetResourceOptions
class, sets the parameter value within it, then passes the options class instance to the operation.
Along with the "options" class, a nested "Builder" class is also provided as a convenient way to
construct instances of the class using the Java "builder" pattern.
Here's an example:
import com.ibm.cloud.sdk.core.http.Response;
import com.ibm.cloud.mysdk.example_service.v1.ExampleService;
import com.ibm.cloud.mysdk.example_service.v1.model.GetResourceOptions;
import com.ibm.cloud.mysdk.example_service.v1.model.Resource;
...
ExampleService myService = /* construct service client instance */
GetResourceOptions options = new GetResourceOptions.Builder()
.resourceId("resource-id-1")
.build();
Response<Resource> response = myService.getResource(options).execute();
Resource result = response.getResult();
Node.js
For Node.js, an interface is defined for each operation, where the name of the interface will be
<operation-name>Params
and it will contain a field for each operation parameter.
Here's an example of a params object for the getResource
operation:
/** Parameters for the `getResource` operation. */
export interface GetResourceParams {
/** The id of the resource to retrieve. */
resourceId: string;
...
}
In this example, the getResource
operation has one parameter - resourceId
.
When invoking this operation, the application creates an instance of the GetResourceParams
interface, sets the parameter value within it, then passes the params object to the operation.
Here's an example:
const params = {
resourceId: 'resource-id-1',
}
myService.getResource(params).then(res => {
...
});
Python
Because the Python language supports named arguments (i.e. `kwargs`) there is no need to define any sort of container structure for parameters in order to allow for future expansion of the API without impacting applications.Here's an example of how the ExampleServiceV1.get_resource()
operation would be invoked:
detailedResponse = my_service.get_resource(resource_id='resource-id-1')
This section contains language-specific information about how an application receives operation responses.
Go
Each operation will return the following values:
result
- An operation-specific response object (if the operation is defined as returning a response object).detailedResponse
- An instance of thecore.DetailedResponse
struct. This will contain the following fields:
StatusCode
- the HTTP status code returned in the response messageHeaders
- the HTTP headers returned in the response message. Keys in the map are canonicalized (see CanonicalHeaderKey)Result
- the operation result (if available). This is the same value returned in theresult
return value mentioned above.RawResult
- the raw response body as a byte array if there was a problem un-marshalling a JSON response body or if the operation was unsuccessful and the response body content is not JSON.
err
- An error object. This will be nil if the operation was successful, or non-nil
if an error occurred.
Here is an example of how to access the response and get additional Information beyond the response object:
result, detailedResponse, err := myService.GetResource(options)
responseHeaders := detailedResponse.GetHeaders()
responseId := responseHeaders.Get("Response-Id")
Java
Each operation will return an instance of com.ibm.sdk.cloud.sdk.core.http.Response<T>
where T
is the class representing the specific
response model associated with the operation (operations that return no response object
will return an instance of com.ibm.sdk.cloud.sdk.core.http.Response<Void>
instead).
Here's an example of how to access that response and get additional information beyond the response object:
// Invoke the operation.
GetResourceOptions options = /* construct options model */
Response<Resource> response = myservice.getResource(options).execute();
// Extract the operation's response model instance (result).
Resource resource = response.getResult();
// Retrieve response headers.
Headers responseHeaders = response.getHeaders();
System.out.println("Response header names: " + responseHeaders.names());
String responseId = responseHeaders.values("response-id").get(0);
Node.js
Each operation will return a response via a Promise. The response is an object containing the result of the operation, HTTP status code and text message, and the HTTP response headers.
Here is an example of how to access the various fields and headers from an operation response:
myService.getResource({
'resourceId': 'resource-id-1',
}).then((response) => {
// result is the response object returned in the response body
// status is the HTTP status code returned in the response
// headers is the set of HTTP headers returned in the response
// statusText is the text message associated with the HTTP status code
const { result, status, headers, statusText } = response;
console.log(JSON.stringify(headers, null, 4));
transactionId = headers['response-id'];
}).catch((err) => {
if (err.status && err.statusText) {
console.log("Error status code: " + err.status + " (" + err.statusText + ")");
}
console.log("Error message: " + err.message);
});
Python
Each operation will return a DetailedResponse instance which encapsulates the operation response object (if applicable), the HTTP status code and response headers.
Here's an example of how to access that response and get additional information beyond the response object:
detailedResponse = my_service.get_resource(resource_id='resource-id-1')
print(detailedResponse)
responseHeaders = detailedResponse.get_headers()
responseId = responseHeaders.get('response-id')
This would display a DetailedResponse
instance having the structure:
{
'result': <response object returned by operation>,
'headers': { <http response headers> },
'status_code': <http status code>
}
You can configure a set of custom HTTP headers in the service client instance and these headers will be included with all requests invoked from that client.
Go
For Go, the custom headers are set on the service client instance by using the
SetDefaultHeaders(http.Header)
function, like this:
customHeaders := http.Header{}
customHeaders.Add("Custom-Header", "custom_value")
myService.SetDefaultHeaders(customHeaders)
// "Custom-Header" will now be included with all requests invoked from "myservice".
Java
For Java, the custom headers are set on the service client instance by using the
setDefaultHeaders(Map<String, String> headers)
function, like this:
Map<String, String> headers = new HashMap<String, String>();
headers.put("Customer-Header", "custom_value");
myService.setDefaultHeaders(headers);
// "Custom-Header" will now be included with all subsequent requests invoked from "myservice".
Node.js
For Node.js, the custom headers are passed via the headers
field of the UserOptions
object
used to construct the service client instance, like this:
import ExampleServiceV1 from 'mysdk/example-service/v1';
const myService = ExampleServiceV1.newInstance({
headers: {
'Custom-Header': 'custom_value',
},
});
// "Custom-Header" will now be included with all requests invoked from "myService".
Python
For Python, the custom headers are set on the service client instance by using the
set_default_headers()
function, like this:
headers = {
'Custom-Header': 'custom_value'
}
my_service.set_default_headers(headers)
# "Custom-Header" will now be included with all requests invoked from "my_service".
You can also pass custom HTTP headers with an individual request.
Go
For Go, just add the custom headers to the "options" struct prior to calling the operation, like this:
options := myService.NewGetResourceOptions("resource-id-1")
customHeaders := make(map[string]string)
customHeaders["Custom-Header"] = "custom_value"
options.SetHeaders(customHeaders)
result, detailedResponse, err := myservice.GetResource(options)
// "Custom-Header" will be sent along with the "GetResource" request.
Java
For Java, just add the custom headers to the ServiceCall
object before calling the execute()
method, like this:
Response<Resource> response = myservice.getResource(options)
.addHeader("Custom-Header", "custom_value")
.execute();
Node.js
For Node.js, just add the custom headers to the "params" object via the optional headers
field prior to calling the operation, like this:
const getResourceParams = {
resourceId: 'resource-id-1',
headers: {
'Custom-Header': 'custom_value',
}
};
myService.getResource(getResourceParams).then(res => {
// Custom-Header will have been sent with this request
});
Python
For Python, just pass the optional headers
parameter when invoking the operation,
like this:
resourceInstance = my_service.get_resource(
resource_id='resource-id-1',
headers={'Custom-Header': 'custom_value'}).get_result()
Each API invocation will receive a response that contains a transaction ID in the HTTP header. This transaction ID can be useful for troubleshooting and accessing relevant logs from your service instance. Check the documentation of the service for the correct field name of this property.
Here are examples of how to retrieve the response header:
Go
result, detailedResponse, err := myService.GetResource(options)
transactionId := detailedResponse.GetHeaders().Get("transaction-id-field-name")
Java
For Java, you can retrieve headers from either the Response<T>
return value of the execute()
method, or from the exception in case of an error:
String transactionId;
try {
Response<Resource> response = myService.getResource(options).execute();
transactionId = response.getHeaders().values("transaction-id-field-name").get(0);
} catch (ServiceResponseException e) {
transactionId = e.getHeaders().values("transaction-id-field-name").get(0);
}
Node.js
const params = {
resourceId: 'resource-id-1',
}
myService.getResource(params).then(
response => {
transactionId = response.headers['transaction-id-field-name'];
},
err => {
transactionId = TBD;
}
);
Python
try:
response = my_service.get_resource(resource_id='resource-id-1')
transaction_id = response.get_headers().get('transaction-id-field-name')
except ApiException as e:
transaction_id = e.http_response.headers.get('transaction-id-field-name')
You have the ability to configure the underlying HTTP client that is used by the SDK to interact with the service endpoint. The sections below describe how this is done for each language.
Go
For Go, you can create your own http.Client
instance and set it on your service client,
like this:
import {
"net/http"
"time"
}
// Construct a new http.Client instance with a user-supplied proxy function.
myHTTPClient := core.DefaultHTTPClient()
myHTTPClient.Transport.Proxy = myProxyFunc
// Set the service's Client field with the SetHTTPClient() method.
myService.Service.SetHTTPClient(myHttpClient)
// This request will use the user-supplied proxy function.
result, detailedResponse, err := myService.GetResource(options)
For more details on configuring the http.Client
instance,
see this link.
Java
For Java, you can configure certain HTTP client options by using the HttpConfigOptions
class
provided by the Java SDK Core library.
After configuring the HttpConfigOptions
instance, use the service client's
configureClient()
method to configure the HTTP client instance held by the service client,
like this:
import java.net.InetSocketAddress;
import java.net.Proxy;
import com.ibm.cloud.sdk.core.http.HttpConfigOptions;
// Configure a proxy in an HttpConfigOptions instance.
Proxy proxy =
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("myproxy.mydomain.com", 8080));
HttpConfigOptions options =
new HttpConfigOptions.Builder().proxy(proxy).build();
myService.configureClient(options);
You can also use the service client's getClient()
and setClient()
methods to supply your own
OkHttpClient
instance, like this:
// Retrieve the current HTTP client instance.
OkHttpClient client = myService.getClient();
// Modify the call timeout to be 5 minutes.
client = client.newBuilder().callTimeout(5, TimeUnit.MINUTES).build();
// Set the new HTTP client on the service.
myService.setClient(client);
For more information about the OkHttpClient
class, please see
this link.
Node.js
For Node.js, you have full control over the HTTPS Agent used to make requests.
This is available for both the service client and the authenticators that make network requests
(e.g. IamAuthenticator
).
Described below are scenarios where this capability is needed.
Note that this functionality is applicable only for Node.js environments - these configurations will have
no effect in the browser.
To invoke requests behind an HTTP proxy (e.g. firewall), a special tunneling agent
must be used.
Use the package tunnel
for this.
Configure this agent with your proxy information, and pass it in as the HTTPS agent in the
service constructor.
Additionally, you must set proxy
to false
in the client constructor.
Here's an example:
const tunnel = require('tunnel');
import ExampleServiceV1 from 'ibm-mysdk/example-service/v1';
import { IamAuthenticator } from 'mysdk/auth';
const myService = new ExampleServiceV1({
authenticator: new IamAuthenticator({ apikey: '{apikey}' }),
httpsAgent: tunnel.httpsOverHttp({
proxy: {
host: 'some.host.org',
port: 1234,
},
}),
proxy: false,
});
To send custom certificates as a security measure in your request, use the cert
, key
,
and/or ca
properties of the HTTPS Agent.
See this documentation
for more information about the options.
Note that the entire contents of the file must be provided - not just the file name.
const tunnel = require('tunnel');
import ExampleServiceV1 from 'ibm-mysdk/example-service/v1';
import { IamAuthenticator } from 'ibm-mysdk/auth';
const certFile = fs.readFileSync('./my-cert.pem');
const keyFile = fs.readFileSync('./my-key.pem');
const myService = new ExampleServiceV1({
authenticator: new IamAuthenticator({
apikey: '{apikey}',
httpsAgent: new https.Agent({
key: keyFile,
cert: certFile,
})
}),
httpsAgent: new https.Agent({
key: keyFile,
cert: certFile,
}),
});
Python
For Python, you can configure certain options in the underlying http client used to
invoke requests (e.g. timeout), you can use the set_http_config()
method, like this:
my_service.set_http_config({'timeout': 120})
response = my_service.get_resource(resource_id='resource-id-1').get_result()
Here is a list of some of the configuration properties that can be set with the set_http_config()
method:
For more information regarding http client configuration, please see this link
This section describes how to configure request timeouts in the various SDKs.
Go
The recommended way to configure request timeouts in a Go SDK is to use an instance of
context.Context
.
Starting with version 3.15.0 of the SDK generator, the Go generator now generates
two methods for each operation, one which accepts a context.Context
instance
as the first parameter and one with only the single "options model" parameter.
Here is an example of how to use the context.Context
to invoke an operation with a
10-second timeout:
// Create a context with a 10-second timeout.
ctx, cancelFunc := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancelFunc()
// Create the resource.
result, detailedResponse, err := myService.CreateResourceWithContext(ctx, createResourceOptionsModel)
When constructing a Context, you receive two values - the context itself, along with the "cancel function".
The returned cancel function can also be invoked in order to cancel an in-flight request.
Note also that by using the context.Context
instance with the <operation-name>WithContext
method,
the timeout applies only to a single operation invocation.
Java
Here is an example of how to set a request timeout in Java:// Retrieve the current HTTP client instance.
OkHttpClient client = myService.getClient();
// Modify the call timeout to be 10 seconds.
client = client.newBuilder().callTimeout(10, TimeUnit.SECONDS).build();
// Set the new HTTP client on the service.
myService.setClient(client);
The request timeout of 10 seconds will be used in each operation invoked using myService1
.
Node
Here is an example of how to set a request timeout in Node:import ExampleServiceV1 from 'ibm-mysdk/example-service/v1';
import { IamAuthenticator } from 'ibm-mysdk/auth';
// Construct an instance of the service with a 10-second timeout (expressed in ms).
const myService = new ExampleServiceV1({
authenticator: new IamAuthenticator({ apikey: '{apikey}' }),
timeout: 10000,
});
The request timeout of 10 seconds will be used in each operation invoked using myService
.
Python
As mentioned in the "Configuring the HTTP Client" section above, you can configure the options in the http client as in this example:# Configure a 10-second request timeout.
my_service.set_http_config({'timeout': 10})
The request timeout of 10 seconds will be used in each operation invoked using my_service
.
Applications that invoke REST APIs sometimes encounter errors such as
429 - Too Many Requests
, 503 - Service Unavailable
, etc.
It can be inconvenient and time-consuming to code around these types of errors in your application.
This section provides information about how to enable automatic retries.
Go
The Go SDK supports a generalized retry feature that can automatically retry common
errors. The default configuration (up to 4 retries with max retry interval of 30 seconds,
along with exponential backoff if no Retry-After
response header is present)
should suffice for most applications, but the retry feature
is customizable to support unique requirements.
Java
The Java SDK supports a limited retry feature that will retry 429 - Too Many Requests
errors
with exponential backoff if no Retry-After
response header is present.
Node
The Node SDK does not currently support automatic retries.
Python
The Python SDK does not currently support automatic retries.
Note: the configuration of automatic retries via external configuration is currently supported only in the Go SDK.
If you are constructing your service client with external configuration properties, you can enable automatic retries (currently supported only in Go SDKs) in the service client by setting properties as in the example below for the "Example Service" service:
export EXAMPLE_SERVICE_URL=https://example-service.cloud.ibm.com/v1
export EXAMPLE_SERVICE_AUTH_TYPE=iam
export EXAMPLE_SERVICE_APIKEY=<iam-api-key>
export EXAMPLE_SERVICE_ENABLE_RETRIES=true
export EXAMPLE_SERVICE_MAX_RETRIES=3
export EXAMPLE_SERVICE_RETRY_INTERVAL=20
If the <service-name>_ENABLE_RETRIES
property is defined as true
, then retries will be enabled.
You can optionally define the <service-name>_MAX_RETRIES
and <service-name>_RETRY_INTERVAL
properties to configure values for the maximum number of retries (default is 4) and maximum retry
interval (default is 30 seconds).
After setting these properties, be sure to construct your service client similar to the examples in the Construct service client section above.
Go
To enable automatic retries programmatically in the Go SDK, use the service client's
EnableRetries()
method, as in this example:
// Construct the service client.
myService, err := exampleservicev1.NewExampleServiceV1(options)
// Enable automatic retries (with max retries 3, max interval 20 secs).
myService.EnableRetries(3, 20 * time.Second)
// Create the resource.
result, detailedResponse, err := myService.CreateResource(createResourceOptionsModel)
In this example, the CreateResource()
operation will be retried up to 3 times
with a maximum retry interval of 20 seconds.
If a "retryable" error response (e.g. 429, 503, etc.) contains
the Retry-After
header, the value of that response header will be used
as the retry interval, subject to a maximum of 20 seconds. If no Retry-After
header
is found in the response, then an exponential backoff policy will be used such
that successive retries use a progressively longer wait time.
Java
Details about this feature in the Java SDK will be added in the future.Node.js
The Node.js SDK currently does not support automatic retries.Python
The Python SDK currently does not support automatic retries.The SDK allows you to configure the HTTP client to disable SSL verification.
Note that this has serious security implications - only do this if you really mean to!
If you are constructing your service client with external configuration properties, you can
disable SSL verification in both the authenticator and the service client by setting
the <service-name>_DISABLE_SSL
(service client) and <service-name>_AUTH_DISABLE_SSL
(authenticator) properties true
.
Here's an example of how to do this for the "Example Service" service:
export EXAMPLE_SERVICE_URL=https://example-service.cloud.ibm.com/v1
export EXAMPLE_SERVICE_AUTH_TYPE=iam
export EXAMPLE_SERVICE_APIKEY=<iam-api-key>
export EXAMPLE_SERVICE_DISABLE_SSL=true
export EXAMPLE_SERVICE_AUTH_DISABLE_SSL=true
After setting these properties, be sure to construct your service client similar to the examples in the Construct service client section above.
Alternatively, you can disable SSL verification programmatically. See the expandable sections below to see how this is done in each language:
Go
For Go, you can disable SSL verification in both the service client and in the authenticator like this:
// Construct authenticator with DisableSSLVerification true
authenticator := &core.IamAuthenticator{
ApiKey: "<iam-api-key>",
DisableSSLVerification: true,
}
// Construct service client
myService, err := exampleservicev1.NewExampleServiceV1(
&exampleservicev1.ExampleServiceV1Options{
Authenticator: authenticator,
}
}
if err != nil {
panic(err)
}
// Disable SSL verification in service client
myService.Service.DisableSSLVerification()
Note: for a given instance of a service client, the "disable SSL" and "automatic retries" features are mutually exclusive. Each feature requires a specific configuration of the underlying HTTP client and are not currently supported simultaneously.
Java - for Java core versions prior to 9.7.0 (deprecated)
For Java, you can disable SSL verification in both the authenticator and service client, like this:
Authenticator authenticator = new IamAuthenticator("<iam-api-key>");
authenticator.setDisableSSLVerification(true);
myService = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
HttpConfigOptions options =
new HttpConfigOptions.Builder().disableSslVerification(true).build();
myService.configureClient(options);
Java - for Java core version 9.7.0 and above
For Java, you can disable SSL verification in both the authenticator and service client, like this:
Authenticator authenticator = new IamAuthenticator.Builder()
.apikey("<iam-api-key>")
.disableSSLVerification(true)
.build();
myService = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
HttpConfigOptions options =
new HttpConfigOptions.Builder().disableSslVerification(true).build();
myService.configureClient(options);
Node.js
For Node.js, set `disableSslVerification` to `true` in the service constructor and/or authenticator constructor, like this:import ExampleServiceV1 from 'ibm-mysdk/example-service/v1';
import { IamAuthenticator } from 'ibm-mysdk/auth';
const myservice = new ExampleServiceV1({
authenticator: new IamAuthenticator({
apikey: '<apikey>',
disableSslVerification: true, // this will disable SSL verification for requests to the IAM token server
}),
disableSslVerification: true, // this will disable SSL verification for any requests invoked with this service client instance
});
Python
For Python, use the `set_disable_ssl_verification()` method on the service client, like this:my_service.set_disable_ssl_verification(True)
When you disable SSL verification, the python urllib3
library might log an excessive number of warning
messages, like this:
.../connectionpool.py:1004: InsecureRequestWarning: Unverified HTTPS request is being made to host '<my-insecure-host>'.
Adding certificate verification is strongly advised.
See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
To suppress these warning messages, you can add this code to your application:
import urllib3
urllib3.disable_warnings()
See this link for more details.
This section provides information about how to deal with errors resulting from request invocations.
The SDK uses standard HTTP response codes to indicate whether a method completed successfully. HTTP response codes in the 2xx range indicate success. A response in the 4xx range is some sort of failure, and a response in the 5xx range usually indicates an internal system error that cannot be resolved by the user.
Go
In the case of an error response from the server endpoint, the Go SDK will do the following:
- The service method (operation) will return a non-nil
error
object. Thiserror
object will contain the error message retrieved from the HTTP response if possible, or a generic error message otherwise. - The
detailedResponse
return value will contain the following fields: -
StatusCode
: the HTTP status code returned in the response.Headers
: the HTTP headers returned in the response.Result
: if the operation returned a JSON error response, this will contain the unmarshalled response in the form of amap[string]interface{}
. This allows the application to examine all of the error information returned in the HTTP response message.RawResult
: if the operation returned a non-JSON error response, this will contain the raw response body as a[]byte
.
Here's an example of checking the error
object after invoking the GetResource
operation:
// Call the GetResource operation and receive the returned Resource.
options := myservice.NewGetResourceOptions("bad-resource-id")
result, detailedResponse, err := myservice.GetResource(options)
if err != nil {
fmt.Println("Error retrieving the resource: ", err.Error())
fmt.Println(" status code: ", detailedResponse.StatusCode)
if (detaliedResponse.Result != nil) {
fmt.Println(" full error response: ", detailedResponse.Result)
} else {
fmt.Println(" raw error response: ", detailedResponse.RawResult)
}
}
Java
In the case of an error response from the server endpoint, the Java SDK will throw an exception
from the com.ibm.cloud.sdk.core.service.exception
package.
All service exceptions contain the following fields:
statusCode
: the HTTP response code that was returned in the responsemessage
: a message that describes the errorheaders
: the HTTP headers returned in the responsedebuggingInfo
: aMap<String, Object>
which contains the unmarshalled error response object in the event that a JSON error response is returned. This will provide more information beyond the error message.
An operation may also throw an IllegalArgumentException
if it detects missing or invalid
input arguments.
Here's an example of how to catch and process specific exceptions that may be returned from an operation:
import com.ibm.cloud.sdk.core.service.exception.BadRequestException;
import com.ibm.cloud.sdk.core.service.exception.NotFoundException;
import com.ibm.cloud.sdk.core.service.exception.RequestTooLargeException;
import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException;
import com.ibm.cloud.sdk.core.service.exception.UnauthorizedException;
...
try {
// Invoke an operation
Response<Resource> response = myService.getResource(options).execute();
// Process response...
} catch (BadRequestException e) {
// Handle Bad Request (400) exception
} catch (UnauthorizedException e) {
// Handle Unauthorized (401) exception
} catch (NotFoundException e) {
// Handle Not Found (404) exception
} catch (RequestTooLargeException e) {
// Handle Request Too Large (413) exception
} catch (ServiceResponseException e) {
// Base class for all exceptions caused by error responses from the service
System.out.println("Service returned status code "
+ e.getStatusCode() + ": " + e.getMessage());
System.out.println("Detailed error info:\n" + e.getDebuggingInfo().toString());
}
Node.js
In the case of an error response from the server endpoint, the Node SDK will
create an Error
object with information that describes the error that occurred.
This error object is passed as the first parameter to the callback function for the method,
and will contain the following fields:
status
: the HTTP status code that was returned in the responsestatusText
: a text description of the status codemessage
: the error message returned in the responsebody
: the error response body. If a JSON response was returned, this will be the stringified response body. If a non-JSON response was returned, this will simply be the raw response body obtained from the response.
Here's an example of how to access the error object:
myService.getResource({
'resourceId': 'bad-resource-id',
}).then((response) => {
// ...handle successful response...
}).catch((err) => {
// ...handle error response...
console.log("Error status code: " + err.status + " (" + err.statusText + ")");
console.log("Error message: " + err.message);
});
Python
In the case of an error response from the server endpoint, the Python SDK will throw an exception with the following fields:
code
: the HTTP status code that was returned in the responsemessage
: a message that describes the errorinfo
: a dictionary of additional information about the error
Here's an example:
try:
response = my_service.get_resource(resource_id='does not exist')
except ApiException as e:
print("Method failed with status code " + str(e.code) + ": " + e.message)
print("Detailed error information: " + e.info)
This section describes how to enable logging within the various SDKs.
Go
Within Go SDKs, a basic logging facility is provided by the Go core library. The Go core's logger supports various logging levels: Error, Info, Warn, Debug. By default, the Go core uses a logger with log level "Error" configured. This means that only error messages will be displayed. A logger configured at log level "Warn" would display "Error" and "Warn" messages (but not "Info" or "Debug"), etc.
To configure the logger to display "Info", "Warn" and "Error" messages, use the core.SetLoggingLevel()
method, as in this example:
// Enable Info logging.
core.SetLoggingLevel(core.LevelInfo)
If you have enabled automatic retries and would like to see some brief messages related to each of the requests that are retried, you can configure a logger at log level "Debug", like this:
// Enable Debug logging.
core.SetLoggingLevel(core.LevelDebug)
// Construct the service client.
myService, err := exampleservicev1.NewExampleServiceV1(options)
// Enable automatic retries.
myService.EnableRetries(3, 20 * time.Second)
// Create the resource.
result, detailedResponse, err := myService.CreateResource(createResourceOptionsModel)
When the "CreateResource()" method is invoked, you should see a handful of debug messages displayed on the console reporting on progress of the request, including any retries that were performed. Here is an example:
2020/10/29 10:34:57 [DEBUG] POST http://example-service.cloud.ibm.com/api/v1/resource
2020/10/29 10:34:57 [DEBUG] POST http://example-service.cloud.ibm.com/api/v1/resource (status: 429): retrying in 1s (5 left)
2020/10/29 10:34:58 [DEBUG] POST http://example-service.cloud.ibm.com/api/v1/resource (status: 429): retrying in 1s (4 left)
In addition to providing a basic logger implementation, the Go core library also defines
a Logger
interface and allows users to supply their own implementation to support unique
logging requirements (perhaps you need messages logged to a file instead of the console).
To use this advanced feature, simply implement the Logger
interface and then call the
SetLogger(Logger)
function to set your implementation as the logger to be used by the
Go core library.
Java
For Java, you can configure the HTTP client's logging detail level by using the `HttpConfigOptions` class.Here's an example:
HttpConfigOptions options =
new HttpConfigOptions.Builder()
.loggingLevel(HttpConfigOptions.LoggingLevel.BASIC)
.build();
myService.configureClient(options);
Node.js
For Node.js, the SDK uses the debug
package for logging.
Specify the desired environment variable to enable logging debug messages.
Python
For Python, you can enable debug logging by importing the logging
package and then setting
the log level to DEBUG as in this example:
import logging
logging.basicConfig(level=logging.DEBUG)
This will cause messages like the following to be logged as your application invokes various operations:
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): iam.cloud.ibm.com:443
DEBUG:urllib3.connectionpool:https://iam.cloud.ibm.com:443 "POST /identity/token HTTP/1.1" 200 1809
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): myservice.cloud.ibm.com:443
DEBUG:urllib3.connectionpool:https://myservice.cloud.ibm.com:443 "POST /myservice/api/v1/resource HTTP/1.1" 201 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): myservice.cloud.ibm.com:443
DEBUG:urllib3.connectionpool:https://myservice.cloud.ibm.com:443 "GET /myservice/api/v1/resource/resource-id-1 HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): myservice.cloud.ibm.com:443
DEBUG:urllib3.connectionpool:https://myservice.cloud.ibm.com:443 "DELETE /myservice/api/v1/resource/resource-id-1 HTTP/1.1" 204 None
To enable detailed logging of request and response messages, you can import the
http.client
package, and then enable debug logging within HTTP connections like this:
from http.client import HTTPConnection
HTTPConnection.debuglevel = 1
For some languages, the SDK supports both synchronous (blocking) and asynchronous (non-blocking) execution of service methods.
Go
The Go SDK supports only synchronous execution of service methods.Java
For Java, all service methods implement the ServiceCall
interface.
To call a method synchronously, use the execute()
method of the ServiceCall<T>
interface,
like this:
// Invoke the operation.
GetResourceOptions options = /* construct options model */
Response<Resource> response = myservice.getResource(options).execute();
// Continue execution...
To call a method asynchronously, use the enqueue()
method of the ServiceCall<T>
interface
to receive a callback when the response arrives.
The ServiceCallback<T>
interface provides onResponse
and onFailure
methods that you
override to handle the callback, like this:
// Invoke the operation in the background
GetResourceOptions options = /* construct options model */
myservice.getResource(options).enqueue(new ServiceCallback<ResourceInstance>() {
@Override
public void onResponse(Response<Resource> response) {
System.out.println("We did it! " + response);
}
@Override
public void onFailure(Exception e) {
System.out.println("Whoops...");
}
});
// Continue execution in the meantime!
Node.js
The Node.js SDK executes each request asynchronously and returns the response as a Promise.Python
The Python SDK supports only synchronous execution of service methods.If you are having difficulties using an SDK or have a question about the IBM Cloud Services, please ask a question at Stack Overflow.
Find more open source projects on the IBM Github Page
This project is released under the Apache 2.0 license. The license's full text can be found in LICENSE.