@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account v3.0, ZNS with Full Type Safety, Consultation Service, Broadcast Service, Group Messaging with List APIs, Social APIs, Enhanced Article Management, Promotion Service v3.0 with Multip
296 lines • 10.3 kB
JavaScript
"use strict";
/**
* Base HTTP client for Zalo API
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseClient = void 0;
const axios_1 = __importStar(require("axios"));
const form_data_1 = __importDefault(require("form-data"));
const common_1 = require("../types/common");
/**
* Base client for making HTTP requests to Zalo API
*/
class BaseClient {
constructor(config) {
// Set default configuration
this.config = {
appId: config.appId,
appSecret: config.appSecret,
timeout: config.timeout || 30000,
debug: config.debug || false,
apiBaseUrl: config.apiBaseUrl || "https://openapi.zalo.me",
retry: {
attempts: config.retry?.attempts || 3,
delay: config.retry?.delay || 1000,
...config.retry,
},
};
this.logger = new common_1.ConsoleLogger(this.config.debug);
// Create axios instance
this.axios = axios_1.default.create({
baseURL: this.config.apiBaseUrl,
timeout: this.config.timeout,
headers: {
"Content-Type": "application/json",
"User-Agent": "RedAI-Zalo-SDK/1.0.0",
},
});
this.setupInterceptors();
}
/**
* Setup axios interceptors for logging and error handling
*/
setupInterceptors() {
// Request interceptor
this.axios.interceptors.request.use((config) => {
this.logger.debug(`Making ${config.method?.toUpperCase()} request to ${config.url}`, {
headers: this.sanitizeHeaders(config.headers || {}),
params: config.params,
});
return config;
}, (error) => {
this.logger.error("Request setup error:", error.message);
return Promise.reject(error);
});
// Response interceptor
this.axios.interceptors.response.use((response) => {
this.logger.debug(`Received response from ${response.config.url}`, {
status: response.status,
data: response.data,
});
return response;
}, (error) => {
this.handleAxiosError(error);
return Promise.reject(error);
});
}
/**
* Sanitize headers for logging (remove sensitive information)
*/
sanitizeHeaders(headers) {
const sanitized = { ...headers };
if (sanitized.access_token) {
sanitized.access_token = "***REDACTED***";
}
if (sanitized.secret_key) {
sanitized.secret_key = "***REDACTED***";
}
return sanitized;
}
/**
* Handle axios errors and log them
*/
handleAxiosError(error) {
if (error.response) {
this.logger.error(`HTTP ${error.response.status} error:`, {
url: error.config?.url,
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
});
}
else if (error.request) {
this.logger.error("No response received:", {
url: error.config?.url,
message: error.message,
});
}
else {
this.logger.error("Request setup error:", error.message);
}
}
/**
* Make a GET request
*/
async get(url, accessToken, params) {
return this.request({
method: "GET",
url,
headers: accessToken ? { access_token: accessToken } : {},
params,
});
}
/**
* Make a POST request
*/
async post(url, accessToken, data, params) {
return this.request({
method: "POST",
url,
headers: accessToken ? { access_token: accessToken } : {},
data,
params,
});
}
/**
* Make a PUT request
*/
async put(url, accessToken, data, params) {
return this.request({
method: "PUT",
url,
headers: accessToken ? { access_token: accessToken } : {},
data,
params,
});
}
/**
* Make a DELETE request
*/
async delete(url, accessToken, params) {
return this.request({
method: "DELETE",
url,
headers: accessToken ? { access_token: accessToken } : {},
params,
});
}
/**
* Upload file using FormData
*/
async uploadFile(url, accessToken, file, filename, additionalFields) {
const formData = new form_data_1.default();
formData.append("file", file, filename);
if (additionalFields) {
Object.entries(additionalFields).forEach(([key, value]) => {
formData.append(key, value);
});
}
return this.request({
method: "POST",
url,
headers: {
access_token: accessToken,
...formData.getHeaders(), // Let form-data package set Content-Type with proper boundary
},
data: formData,
});
}
/**
* Make a generic HTTP request with retry logic
*/
async request(config) {
let lastError;
for (let attempt = 1; attempt <= (this.config.retry?.attempts || 3); attempt++) {
try {
const axiosConfig = {
method: config.method,
url: config.url,
headers: config.headers,
params: config.params,
data: config.data,
timeout: config.timeout || this.config.timeout,
};
const response = await this.axios.request(axiosConfig);
// Check for Zalo API errors in response
this.validateZaloResponse(response.data);
return response.data;
}
catch (error) {
lastError = error;
if (attempt < (this.config.retry?.attempts || 3) &&
this.shouldRetry(error)) {
this.logger.warn(`Request failed, retrying (${attempt}/${this.config.retry?.attempts || 3})...`);
await this.delay((this.config.retry?.delay || 1000) * attempt);
continue;
}
break;
}
}
throw this.createSDKError(lastError);
}
/**
* Validate Zalo API response for errors
*/
validateZaloResponse(data) {
if (data && typeof data === "object") {
// Check for standard Zalo error format
if ("error" in data && data.error !== 0) {
const errorMessage = data.error_description || data.message || "Unknown Zalo API error";
throw new common_1.ZaloSDKError(errorMessage, data.error, data);
}
// Check for nested result error format
if ("result" in data &&
data.result &&
typeof data.result === "object" &&
"error" in data.result &&
data.result.error !== 0) {
const errorMessage = data.result.error_description ||
data.result.message ||
"Unknown Zalo API error";
throw new common_1.ZaloSDKError(errorMessage, data.result.error, data.result);
}
}
}
/**
* Determine if request should be retried
*/
shouldRetry(error) {
// Retry on network errors or 5xx server errors
return (!error.response ||
(error.response.status >= 500 && error.response.status < 600));
}
/**
* Create SDK error from axios error
*/
createSDKError(error) {
if (error instanceof common_1.ZaloSDKError) {
return error;
}
if (error instanceof axios_1.AxiosError) {
const response = error.response;
if (response?.data) {
const errorCode = response.data.error || response.status;
const errorMessage = response.data.error_description ||
response.data.message ||
error.message;
return new common_1.ZaloSDKError(errorMessage, errorCode, response.data);
}
}
return new common_1.ZaloSDKError(error.message, -1, error);
}
/**
* Delay execution for specified milliseconds
*/
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
exports.BaseClient = BaseClient;
//# sourceMappingURL=base-client.js.map