admitad-api-client
Version:
A TypeScript/JavaScript client for the Admitad API with built-in authentication and token management
193 lines • 7.23 kB
JavaScript
import { encodeCredentials, createAuthHeader, isTokenExpired, calculateExpirationTime } from '../utils/auth.js';
export class AdmitadApiClient {
constructor(config) {
this.accessToken = null;
this.refreshToken = null;
this.tokenExpiresAt = null;
this.config = {
baseUrl: 'https://api.admitad.com',
...config
};
// Use provided base64Auth or generate it from clientId and clientSecret
this.base64Auth = config.base64Auth || encodeCredentials(config.clientId, config.clientSecret);
}
/**
* Authenticates with Admitad API and obtains access token
* @param scopes - Array of scopes to request access for
* @returns Promise resolving to token response
*/
async authenticate(scopes) {
const url = `${this.config.baseUrl}/token/`;
const scope = scopes.join(' ');
const requestData = {
client_id: this.config.clientId,
scope,
grant_type: 'client_credentials'
};
const body = new URLSearchParams(requestData).toString();
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': createAuthHeader(this.base64Auth),
'Content-Type': 'application/x-www-form-urlencoded'
},
body
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Authentication failed: ${errorData.error} - ${errorData.error_description || 'Unknown error'}`);
}
const tokenData = await response.json();
// Store token data
this.accessToken = tokenData.access_token;
this.refreshToken = tokenData.refresh_token;
this.tokenExpiresAt = calculateExpirationTime(tokenData.expires_in);
return tokenData;
}
/**
* Ensures we have a valid access token, refreshing if necessary
*/
async ensureValidToken() {
if (!this.accessToken || !this.tokenExpiresAt || isTokenExpired(this.tokenExpiresAt)) {
throw new Error('No valid token available. Please authenticate first.');
}
}
/**
* Makes an authenticated request to the Admitad API
* @param endpoint - API endpoint (without base URL)
* @param options - Request options
* @returns Promise resolving to response data
*/
async request(endpoint, options = {}) {
await this.ensureValidToken();
const url = new URL(endpoint, this.config.baseUrl);
// Add query parameters if provided
if (options.params) {
Object.entries(options.params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
const headers = {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
...options.headers
};
const requestOptions = {
method: options.method || 'GET',
headers
};
if (options.body && (options.method === 'POST' || options.method === 'PUT')) {
requestOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), requestOptions);
if (!response.ok) {
let errorMessage = `Request failed with status ${response.status}`;
try {
const errorData = await response.json();
errorMessage = errorData.error || errorData.message || errorMessage;
}
catch {
// If we can't parse error as JSON, use the status text
errorMessage = response.statusText || errorMessage;
}
throw new Error(errorMessage);
}
// Handle empty responses
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
return await response.text();
}
/**
* Gets current access token
*/
getAccessToken() {
return this.accessToken;
}
/**
* Gets current refresh token
*/
getRefreshToken() {
return this.refreshToken;
}
/**
* Checks if current token is valid and not expired
*/
isAuthenticated() {
return !!(this.accessToken && this.tokenExpiresAt && !isTokenExpired(this.tokenExpiresAt));
}
/**
* Clears stored authentication data
*/
logout() {
this.accessToken = null;
this.refreshToken = null;
this.tokenExpiresAt = null;
}
/**
* Gets commission rates for AliExpress products
* @param urls - Array of product URLs to check commission rates for
* @returns Promise resolving to commission rates response
*/
async getAliExpressCommissionRates(urls) {
const requestBody = { urls };
return await this.request('/aliexpress/commission_rates/', {
method: 'POST',
body: requestBody
});
}
/**
* Shortens an Admitad URL using the URL shortener service
* @param link - The Admitad link to shorten (must belong to Admitad's domains)
* @returns Promise resolving to shortened URL response
*/
async shortenUrl(link) {
const requestBody = { link };
return await this.request('/shortlink/modify/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams(requestBody).toString()
});
}
/**
* Generates deeplinks for affiliate programs
* @param websiteId - The ad space ID (w_id)
* @param campaignId - The affiliate program ID (c_id)
* @param params - Deeplink generation parameters
* @returns Promise resolving to generated deeplinks
*/
async generateDeeplinks(websiteId, campaignId, params) {
const endpoint = `/deeplink/${websiteId}/advcampaign/${campaignId}/`;
// Convert ulp to array if it's a string
const ulpArray = Array.isArray(params.ulp) ? params.ulp : [params.ulp];
// Build query parameters
const queryParams = {};
// Add subid parameters if provided
if (params.subid)
queryParams.subid = params.subid;
if (params.subid1)
queryParams.subid1 = params.subid1;
if (params.subid2)
queryParams.subid2 = params.subid2;
if (params.subid3)
queryParams.subid3 = params.subid3;
if (params.subid4)
queryParams.subid4 = params.subid4;
// Add multiple ulp parameters
const searchParams = new URLSearchParams();
Object.entries(queryParams).forEach(([key, value]) => {
searchParams.append(key, value);
});
ulpArray.forEach(ulp => {
searchParams.append('ulp', ulp);
});
return await this.request(endpoint, {
method: 'GET',
params: Object.fromEntries(searchParams.entries())
});
}
}
//# sourceMappingURL=admitad-client.js.map