fix(core): Switch to SmartRequest fluent API and improve Cloudflare API request handling
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-17 - 6.4.2 - fix(core)
|
||||||
|
Switch to SmartRequest fluent API and improve Cloudflare API request handling
|
||||||
|
|
||||||
|
- Upgrade runtime dependencies: @push.rocks/smartlog -> ^3.1.10, @push.rocks/smartrequest -> ^5.0.1, @push.rocks/smartstring -> ^4.1.0, @tsclass/tsclass -> ^9.3.0, cloudflare -> ^5.2.0
|
||||||
|
- Upgrade devDependencies: @git.zone/tsbuild -> ^3.1.0, @git.zone/tsrun -> ^2.0.0, @git.zone/tstest -> ^2.8.2, @push.rocks/qenv -> ^6.1.3, openapi-typescript -> ^7.10.1
|
||||||
|
- Export SmartRequest and CoreResponse from cloudflare.plugins to align with smartrequest v5 API
|
||||||
|
- Refactor CloudflareAccount.request to use SmartRequest fluent builder, add detailed logging, default JSON Content-Type, support multipart/form-data via formData(), and use appropriate HTTP method helpers
|
||||||
|
- Improve response parsing: return a safe fallback when JSON parsing fails by reading response.text() and include a concise message; better HTTP error logging including response body text
|
||||||
|
- Update usages to rely on the new request behavior (zones/workers managers use account.request for endpoints not covered by the official client)
|
||||||
|
|
||||||
## 2025-04-30 - 6.4.1 - fix(ci)
|
## 2025-04-30 - 6.4.1 - fix(ci)
|
||||||
Update CI workflows, repository URL, and apply minor code formatting fixes
|
Update CI workflows, repository URL, and apply minor code formatting fixes
|
||||||
|
|
||||||
|
|||||||
21
package.json
21
package.json
@@ -36,21 +36,20 @@
|
|||||||
"homepage": "https://gitlab.com/mojoio/cloudflare#readme",
|
"homepage": "https://gitlab.com/mojoio/cloudflare#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/smartdelay": "^3.0.1",
|
"@push.rocks/smartdelay": "^3.0.1",
|
||||||
"@push.rocks/smartlog": "^3.0.2",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrequest": "^2.1.0",
|
"@push.rocks/smartrequest": "^5.0.1",
|
||||||
"@push.rocks/smartstring": "^4.0.5",
|
"@push.rocks/smartstring": "^4.1.0",
|
||||||
"@tsclass/tsclass": "^9.1.0",
|
"@tsclass/tsclass": "^9.3.0",
|
||||||
"cloudflare": "^4.2.0"
|
"cloudflare": "^5.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^2.3.2",
|
"@git.zone/tsbuild": "^3.1.0",
|
||||||
"@git.zone/tsrun": "^1.3.3",
|
"@git.zone/tsrun": "^2.0.0",
|
||||||
"@git.zone/tstest": "^1.0.96",
|
"@git.zone/tstest": "^2.8.2",
|
||||||
"@push.rocks/qenv": "^6.1.0",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/tapbundle": "^6.0.0",
|
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^22.15.3",
|
||||||
"openapi-typescript": "^7.6.1"
|
"openapi-typescript": "^7.10.1"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"ts/**/*",
|
"ts/**/*",
|
||||||
|
|||||||
7654
pnpm-lock.yaml
generated
7654
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
// tslint:disable-next-line: no-implicit-dependencies
|
// tslint:disable-next-line: no-implicit-dependencies
|
||||||
import { expect, tap } from '@push.rocks/tapbundle';
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
// tslint:disable-next-line: no-implicit-dependencies
|
// tslint:disable-next-line: no-implicit-dependencies
|
||||||
import { Qenv } from '@push.rocks/qenv';
|
import { Qenv } from '@push.rocks/qenv';
|
||||||
|
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@apiclient.xyz/cloudflare',
|
name: '@apiclient.xyz/cloudflare',
|
||||||
version: '6.4.1',
|
version: '6.4.2',
|
||||||
description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.'
|
description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,56 +42,77 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
customHeaders?: Record<string, string>,
|
customHeaders?: Record<string, string>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const options: plugins.smartrequest.ISmartRequestOptions = {
|
logger.log('debug', `Making ${method} request to ${endpoint}`);
|
||||||
method,
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${this.authToken}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...customHeaders,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Build the request using fluent API
|
||||||
|
let requestBuilder = plugins.SmartRequest.create()
|
||||||
|
.url(`https://api.cloudflare.com/client/v4${endpoint}`)
|
||||||
|
.header('Authorization', `Bearer ${this.authToken}`);
|
||||||
|
|
||||||
|
// Add custom headers
|
||||||
|
if (customHeaders) {
|
||||||
|
for (const [key, value] of Object.entries(customHeaders)) {
|
||||||
|
requestBuilder = requestBuilder.header(key, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default to JSON content type if no custom headers
|
||||||
|
requestBuilder = requestBuilder.header('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add request body if provided
|
||||||
if (data) {
|
if (data) {
|
||||||
if (customHeaders && customHeaders['Content-Type']?.includes('multipart/form-data')) {
|
if (customHeaders && customHeaders['Content-Type']?.includes('multipart/form-data')) {
|
||||||
// For multipart form data, use the data directly as the request body
|
// For multipart form data, use formData method
|
||||||
options.requestBody = data;
|
requestBuilder = requestBuilder.formData(data);
|
||||||
} else {
|
} else {
|
||||||
// For JSON requests, stringify the data
|
// For JSON requests, use json method
|
||||||
options.requestBody = JSON.stringify(data);
|
requestBuilder = requestBuilder.json(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('debug', `Making ${method} request to ${endpoint}`);
|
// Execute the request with the appropriate method
|
||||||
const response = await plugins.smartrequest.request(
|
let response: plugins.CoreResponse;
|
||||||
`https://api.cloudflare.com/client/v4${endpoint}`,
|
switch (method) {
|
||||||
options,
|
case 'GET':
|
||||||
);
|
response = await requestBuilder.get();
|
||||||
|
break;
|
||||||
// Check if response is already an object (might happen with newer smartrequest versions)
|
case 'POST':
|
||||||
if (typeof response.body === 'object' && response.body !== null) {
|
response = await requestBuilder.post();
|
||||||
return response.body;
|
break;
|
||||||
|
case 'PUT':
|
||||||
|
response = await requestBuilder.put();
|
||||||
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
response = await requestBuilder.delete();
|
||||||
|
break;
|
||||||
|
case 'PATCH':
|
||||||
|
response = await requestBuilder.patch();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported HTTP method: ${method}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try to parse as JSON
|
// Check response status
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorBody = await response.text();
|
||||||
|
logger.log('error', `HTTP ${response.status}: ${errorBody}`);
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the response body
|
||||||
try {
|
try {
|
||||||
if (typeof response.body === 'string' && response.body.trim()) {
|
return await response.json<T>();
|
||||||
return JSON.parse(response.body);
|
|
||||||
} else {
|
|
||||||
// If body is empty or not a string, return an empty result
|
|
||||||
logger.log('warn', `Empty or invalid response body: ${typeof response.body}`);
|
|
||||||
return { result: [] } as T;
|
|
||||||
}
|
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
logger.log('warn', `Failed to parse response as JSON: ${parseError.message}`);
|
logger.log('warn', `Failed to parse response as JSON: ${parseError.message}`);
|
||||||
|
|
||||||
// Create a fake response object to maintain expected structure
|
// Try to get as text and create a fallback response
|
||||||
|
const textBody = await response.text().catch(() => '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: [],
|
result: [],
|
||||||
success: true,
|
success: true,
|
||||||
errors: [],
|
errors: [],
|
||||||
messages: [
|
messages: [`Failed to parse: ${textBody.substring(0, 50)}...`],
|
||||||
`Failed to parse: ${typeof response.body === 'string' ? response.body?.substring(0, 50) : typeof response.body}...`,
|
|
||||||
],
|
|
||||||
} as T;
|
} as T;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as smartlog from '@push.rocks/smartlog';
|
import * as smartlog from '@push.rocks/smartlog';
|
||||||
import * as smartpromise from '@push.rocks/smartpromise';
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
import * as smartdelay from '@push.rocks/smartdelay';
|
import * as smartdelay from '@push.rocks/smartdelay';
|
||||||
import * as smartrequest from '@push.rocks/smartrequest';
|
import { SmartRequest, CoreResponse } from '@push.rocks/smartrequest';
|
||||||
import * as smartstring from '@push.rocks/smartstring';
|
import * as smartstring from '@push.rocks/smartstring';
|
||||||
import * as tsclass from '@tsclass/tsclass';
|
import * as tsclass from '@tsclass/tsclass';
|
||||||
|
|
||||||
export { smartlog, smartpromise, smartdelay, smartrequest, smartstring, tsclass };
|
export { smartlog, smartpromise, smartdelay, SmartRequest, CoreResponse, smartstring, tsclass };
|
||||||
|
|
||||||
// third party
|
// third party
|
||||||
import * as cloudflare from 'cloudflare';
|
import * as cloudflare from 'cloudflare';
|
||||||
|
|||||||
Reference in New Issue
Block a user