Migrate to the new MAPI
Background
We are moving towards using API Gateway for MAPI to replace our current custom service setup. The current MAPI is now considered deprecated and will be discontinued by 02.01.2025.
Here are the primary reasons for this change:
- Enhanced Performance and Stability: The move aims to boost system performance and provide a more reliable operational environment.
- Strengthened Security: By adopting API Gateway and using OAuth2, we aim to improve security measures, reducing risks and better protecting our data.
- Reduced Maintenance: The new setup is expected to require less maintenance, making operations more efficient.
- Improved Control Over Request Rates: This change will allow for better management of traffic, helping to handle request rates more effectively.
- Better Reporting: The transition will enhance reporting capabilities, providing clearer insights and analytics.
Migration
Authentication
The current version of MAPI authenticates requests using an API key in the header. However, the new MAPI will shift to an OAuth2 flow for authentication.
This change requires clients to first obtain a JWT access token using an ID and secret. This token will then need to be included in all subsequent calls to MAPI.
Authentication URL's
Environment | URL |
---|---|
Production EU | https://idp.bluestonepim.com/op/token |
Production US | https://idp-us.bluestonepim.com/op/token |
Test | https://idp.test.bluestonepim.com/op/token |
Token expiration
The access token is valid for 3600 seconds. You can inspect it to see details like when it was issued and when it will expire.
Request URL changes
In the Management API (MAPI), the structure of request URLs is composed of three distinct parts.
To illustrate, let's use the URL responsible for retrieving a product from PIM based on its ID:
Example URL (Production environment):
https://mapi.bluestonepim.com/pim/products/id
The breakdown of this URL is as follows:
Part | Example |
---|---|
Hostname | mapi.bluestonepim.com |
Service path | pim |
Resource path | products/{id} |
Hostname
The hostname will be changed in the following way:
Environment | Current MAPI | New MAPI |
---|---|---|
Production EU | mapi.bluestonepim.com | api.bluestonepim.com |
Production US | mapi-us-e1.bluestonepim.com | api-us.bluestonepim.com |
Test | mapi.test.bluestonepim.com | api.test.bluestonepim.com |
Service Path
The service path specifies the service to which the request is directed.
The following services will get new paths:
Service | Old path | New path |
---|---|---|
Bluestone MediaBank | media | media-bank |
bs-core-search | core-search | search |
CMS | cms-context | cms |
Resource Path
This section represents the exact resource within the chosen service. During this migration, resource paths will remain consistent and will not be altered.
Throttling and Rate Limits
In order to ensure high performance and maintain a fair usage policy, the new Management API (MAPI) incorporate throttling mechanisms. These mechanisms control the rate at which API requests can be made, ensuring that the infrastructure remains responsive and available to all users.
The exact limits applicable to your integrations are stipulated in the contract agreement with Bluestone PIM.
The limits apply to all clients combined within a customer organization.
Limits
- Rate Limit: A predefined number of requests are allowed per second (RPS). For instance, if the rate limit is set to 10 RPS, this means you can make up to 10 requests in one second.
- Burst Limit: Beyond the steady rate limit we support a burst of up to 10 additional RPS. This provides flexibility for scenarios where short-term, higher request volumes might be needed. For example, if you do 0 requests one second, you can do 20 requests the next second. If you do 9 requests one second, you can do 11 requests the next second and so on.
Handling Limit Exceedance
Should you exceed the set rate limits you will receive an HTTP 429 response, indicating "Too Many Requests".
It's essential to implement a backoff strategy in your integration. A backoff strategy typically involves:
-
- Detecting when a 429 status code is received.
- Waiting for a predetermined duration before making the next request.
- Gradually increasing the wait time if subsequent requests also result in a 429 response.
This approach not only helps in complying with the rate limits but also ensures that your application remains resilient and adaptive to varying API conditions.
In conclusion, while throttling might seem like a constraint, it's a necessary measure to ensure equitable access, maintain service performance, and promote good API consumer habits. Make sure to monitor your API request patterns and adjust them as needed to fit within the provided limits. If you find that this is not possible, please contact Bluestone PIM about increasing the limits.
Examples
Basic
This Python example shows how to get an access token and fetch all catalogs in your organization.
import requests
import json
# Constants
AUTH_URL = 'https://idp.test.bluestonepim.com/op/token'
CLIENT_ID = 'YOUR_ID'
CLIENT_SECRET = 'YOUR_SECRET'
BASE_URL = "https://api.test.bluestonepim.com"
def get_token(auth_url, client_id, client_secret):
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = {
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
}
response = requests.post(auth_url, headers=headers, data=data)
auth_json = response.json()
return auth_json['access_token']
def request_api(base_url, endpoint, method, access_token, payload=None):
url = f"{base_url}/{endpoint}"
headers = {
'context-fallback': 'true',
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
response = requests.request(method, url, headers=headers, json=payload)
return response.json()
# Usage
access_token = get_token(AUTH_URL, CLIENT_ID, CLIENT_SECRET)
endpoint = "pim/catalogs"
method = "GET"
response_json = request_api(BASE_URL, endpoint, method, access_token)
print(response_json)
Advanced
This Python example also shows how to get an access token and fetch all catalogs in your organization. The script is structured to handle common error conditions that might occur when interacting with an API, like network issues, server errors, or authentication problems, and employs retry logic with backoff to increase robustness and resilience in the face of transient errors. There is a more detailed explanation below the code.
import requests
import time
from datetime import datetime, timedelta
# Constants
AUTH_URL = 'https://idp.test.bluestonepim.com/op/token'
CLIENT_ID = 'YOUR_ID'
CLIENT_SECRET = 'YOUR_SECRET'
BASE_URL = "https://api.test.bluestonepim.com"
TIMEOUT = 10 # Request timeout in seconds
RETRY_STATUSES = {429, 500, 502, 503, 504} # HTTP response statuses to retry
TOKEN_RETRIES = 3 # Number of retries for fetching the token
# Global variable to store token information
token_info = None
def get_token():
global token_info
# If we have a token and it's not expired, reuse it
if token_info and token_info['expiry_time'] > datetime.now():
return token_info['access_token']
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = {
'grant_type': 'client_credentials',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
}
for retry in range(TOKEN_RETRIES):
try:
response = requests.post(AUTH_URL, headers=headers, data=data, timeout=TIMEOUT)
if response.status_code in RETRY_STATUSES and retry < TOKEN_RETRIES - 1:
print(f"Received {response.status_code} while fetching token, waiting 3 seconds before retrying...")
time.sleep(3)
continue
response.raise_for_status()
auth_json = response.json()
expiry_time = datetime.now() + timedelta(seconds=auth_json['expires_in'])
token_info = {'access_token': auth_json['access_token'], 'expiry_time': expiry_time}
return token_info['access_token']
except requests.exceptions.RequestException as e:
print(f"An error occurred while fetching token: {e}")
if retry >= TOKEN_RETRIES - 1:
print("Max retries to get token exceeded.")
raise e
def request_api(endpoint, method, payload=None, retries=5, backoff_factor=2):
url = f"{BASE_URL}/{endpoint}"
access_token = get_token()
for retry in range(retries):
headers = {
'context-fallback': 'true',
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
try:
response = requests.request(method, url, headers=headers, json=payload, timeout=TIMEOUT)
# Only retry for 429 Too Many Requests or certain 5xx server errors
if response.status_code in RETRY_STATUSES and retry < retries - 1:
print(f"Received {response.status_code}, retrying...")
# Apply exponential backoff
time.sleep(backoff_factor ** retry)
continue
response.raise_for_status() # Raise HTTPError for bad responses (4xx and 5xx)
return response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
print("Method parameters", endpoint, method, payload)
raise e
# Usage
endpoint = "pim/catalogs"
method = "GET"
response_json = request_api(endpoint, method)
print(response_json)
Here's a breakdown of what each part of the code does:
-
Constants and Global Variable:
- The script begins by defining several constants and a global variable to hold the authentication token information. The constants include URLs, client credentials, a request timeout value, a set of HTTP status codes to retry on, and a number of retries for fetching the token.
-
get_token
Function:- This function is responsible for obtaining an authentication token from the authentication server.
- It first checks if there's an existing token that hasn't expired yet, reusing it if possible to avoid unnecessary requests.
- If no valid token is available, it makes a POST request to the authentication server, retrying up to
TOKEN_RETRIES
times if certain error conditions are encountered (like a timeout or one of the specified retryable HTTP status codes). - A simple 3-second wait is introduced before retrying to fetch the token if a retryable error occurs.
- If a token is successfully obtained, the function updates the global
token_info
variable with the new token and its expiry time, then returns the token. - If the maximum number of retries is exceeded or other request errors occur, it prints an error message to the console and raises the exception to halt execution.
-
request_api
Function:- This function is designed to make a request to a specified endpoint of the API.
- It first calls
get_token
to ensure it has a valid authentication token. - It then attempts to make the API request, retrying up to a specified number of times (
retries
) if a retryable error condition occurs (like a 429 Too Many Requests or certain 500-level server errors). - An exponential backoff strategy is employed to wait increasingly longer amounts of time before each retry, helping to alleviate load on the server and increase the likelihood of a successful request on subsequent retries.
- If a request error occurs, it prints an error message along with the method parameters to the console for debugging purposes, and then raises the exception to halt execution.
-
Usage:
- Finally, the script demonstrates how to use the
request_api
function to make a GET request to a specific endpoint of the API. - It prints the response json to the console.
- Finally, the script demonstrates how to use the
Updated 5 months ago