Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ They provide common runtime utilities needed by SDKs to make API calls and handl
| [@apimatic/test-utilities](packages/test-utilities) | [![npm shield](https://img.shields.io/npm/v/@apimatic/test-utilities)](https://www.npmjs.com/package/@apimatic/test-utilities) | Provides assertion utilities for testing api calls. It can be plugged in as dev dependency to any library. |
| [@apimatic/pagination](packages/pagination) | [![npm shield](https://img.shields.io/npm/v/@apimatic/pagination)](https://www.npmjs.com/package/@apimatic/pagination) | Provides utilities to handle paginated API responses, including support for asynchronous iteration over pages or items. |
| [@apimatic/proxy](packages/proxy) | [![npm shield](https://img.shields.io/npm/v/@apimatic/proxy)](https://www.npmjs.com/package/@apimatic/proxy) | Provides proxy configuration utilities for HTTP clients. |
| [@apimatic/hmac-signature-verifier](packages/hmac-signature-verifier) | [![npm shield](https://img.shields.io/npm/v/@apimatic/hmac-signature-verifier)](https://www.npmjs.com/package/@apimatic/hmac-signature-verifier) | Provides HMAC signature verification utilities to secure HTTP requests. |

## Builds and Usage

Expand Down
8 changes: 5 additions & 3 deletions packages/authentication-adapters/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

### [0.5.13-alpha.1](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/authentication-adapters@0.5.12...@apimatic/authentication-adapters@0.5.13-alpha.1) (2025-09-29)
### [0.5.14](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/authentication-adapters@0.5.13...@apimatic/authentication-adapters@0.5.14) (2025-11-04)

**Note:** Version bump only for package @apimatic/authentication-adapters
### Bug Fixes

- set headers for file and JSON data parts in multipart from request ([#304](https://github.com/apimatic/apimatic-js-runtime/issues/304)) ([10efc46](https://github.com/apimatic/apimatic-js-runtime/commit/10efc469f964a8115070ae6cf366245acb970696))

### [0.5.13-alpha.0](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/authentication-adapters@0.5.12...@apimatic/authentication-adapters@0.5.13-alpha.0) (2025-09-29)
### [0.5.13](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/authentication-adapters@0.5.12...@apimatic/authentication-adapters@0.5.13) (2025-09-30)

**Note:** Version bump only for package @apimatic/authentication-adapters

Expand Down
8 changes: 4 additions & 4 deletions packages/authentication-adapters/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@apimatic/authentication-adapters",
"author": "APIMatic Ltd.",
"version": "0.5.13-alpha.1",
"version": "0.5.14",
"license": "MIT",
"sideEffects": false,
"main": "lib/index.js",
Expand Down Expand Up @@ -55,9 +55,9 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@apimatic/core-interfaces": "^0.2.13-alpha.1",
"@apimatic/http-headers": "^0.3.8-alpha.1",
"@apimatic/http-query": "^0.3.8-alpha.1",
"@apimatic/core-interfaces": "^0.2.14",
"@apimatic/http-headers": "^0.3.8",
"@apimatic/http-query": "^0.3.9",
"tslib": "^2.8.1"
},
"publishConfig": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callHttpInterceptors } from '../../core/src/http/httpInterceptor';
import { callHttpInterceptors } from '@apimatic/core';
import { accessTokenAuthenticationProvider } from '../src/accessTokenAdapter';
import { HttpRequest } from '../../core-interfaces/src/httpRequest';
import { HttpResponse } from '../../core-interfaces/src/httpResponse';
import { HttpRequest, HttpResponse } from '@apimatic/core-interfaces';

describe('test access token authentication scheme', () => {
const config = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callHttpInterceptors } from '../../core/src/http/httpInterceptor';
import { callHttpInterceptors } from '@apimatic/core';
import { basicAuthenticationProvider } from '../src/basicAuthenticationAdapter';
import { HttpRequest } from '../../core-interfaces/src/httpRequest';
import { HttpResponse } from '../../core-interfaces/src/httpResponse';
import { HttpRequest, HttpResponse } from '@apimatic/core-interfaces';

describe('test basic authentication scheme', () => {
const config = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callHttpInterceptors } from '../../core/src/http/httpInterceptor';
import { callHttpInterceptors } from '@apimatic/core';
import { customHeaderAuthenticationProvider } from '../src/customHeaderAuthenticationAdapter';
import { HttpRequest } from '../../core-interfaces/src/httpRequest';
import { HttpResponse } from '../../core-interfaces/src/httpResponse';
import { HttpRequest, HttpResponse } from '@apimatic/core-interfaces';

describe('test custom header authentication scheme', () => {
test.each([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callHttpInterceptors } from '../../core/src/http/httpInterceptor';
import { callHttpInterceptors } from '@apimatic/core';
import { customQueryAuthenticationProvider } from '../src/customQueryAuthenticationAdapter';
import { HttpRequest } from '../../core-interfaces/src/httpRequest';
import { HttpResponse } from '../../core-interfaces/src/httpResponse';
import { HttpRequest, HttpResponse } from '@apimatic/core-interfaces';

describe('test custom query authentication scheme', () => {
test.each([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callHttpInterceptors } from '../../core/src/http/httpInterceptor';
import { callHttpInterceptors } from '@apimatic/core';
import { noneAuthenticationProvider } from '../src/noAuthenticationAdapter';
import { HttpRequest } from '../../core-interfaces/src/httpRequest';
import { HttpResponse } from '../../core-interfaces/src/httpResponse';
import { HttpRequest, HttpResponse } from '@apimatic/core-interfaces';

describe('test access token authentication scheme', () => {
const response: HttpResponse = {
Expand Down
8 changes: 5 additions & 3 deletions packages/axios-client-adapter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

### [0.3.19-alpha.1](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/axios-client-adapter@0.3.18...@apimatic/axios-client-adapter@0.3.19-alpha.1) (2025-09-29)
### [0.3.20](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/axios-client-adapter@0.3.19...@apimatic/axios-client-adapter@0.3.20) (2025-11-04)

**Note:** Version bump only for package @apimatic/axios-client-adapter
### Bug Fixes

- set headers for file and JSON data parts in multipart from request ([#304](https://github.com/apimatic/apimatic-js-runtime/issues/304)) ([10efc46](https://github.com/apimatic/apimatic-js-runtime/commit/10efc469f964a8115070ae6cf366245acb970696))

### [0.3.19-alpha.0](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/axios-client-adapter@0.3.18...@apimatic/axios-client-adapter@0.3.19-alpha.0) (2025-09-29)
### [0.3.19](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/axios-client-adapter@0.3.18...@apimatic/axios-client-adapter@0.3.19) (2025-09-30)

**Note:** Version bump only for package @apimatic/axios-client-adapter

Expand Down
14 changes: 7 additions & 7 deletions packages/axios-client-adapter/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@apimatic/axios-client-adapter",
"author": "APIMatic Ltd.",
"version": "0.3.19-alpha.1",
"version": "0.3.20",
"license": "MIT",
"sideEffects": false,
"main": "lib/index.js",
Expand Down Expand Up @@ -58,13 +58,13 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@apimatic/convert-to-stream": "^0.1.8-alpha.1",
"@apimatic/core-interfaces": "^0.2.13-alpha.1",
"@apimatic/file-wrapper": "^0.3.8-alpha.1",
"@apimatic/http-headers": "^0.3.8-alpha.1",
"@apimatic/http-query": "^0.3.8-alpha.1",
"@apimatic/convert-to-stream": "^0.1.8",
"@apimatic/core-interfaces": "^0.2.14",
"@apimatic/file-wrapper": "^0.3.9",
"@apimatic/http-headers": "^0.3.8",
"@apimatic/http-query": "^0.3.9",
"@apimatic/json-bigint": "^1.2.0",
"@apimatic/proxy": "^0.1.3-alpha.1",
"@apimatic/proxy": "^0.1.3",
"axios": "^1.8.4",
"detect-browser": "^5.3.0",
"detect-node": "^2.1.0",
Expand Down
32 changes: 30 additions & 2 deletions packages/axios-client-adapter/src/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import FormData from 'form-data';
import {
CONTENT_TYPE_HEADER,
FORM_URLENCODED_CONTENT_TYPE,
lookupCaseInsensitive,
} from '@apimatic/http-headers';
import {
HttpRequest,
HttpResponse,
isFormDataWrapper,
RetryConfiguration,
} from '@apimatic/core-interfaces';
import { urlEncodeKeyValuePairs } from '@apimatic/http-query';
Expand Down Expand Up @@ -107,7 +109,16 @@ export class HttpClient {
});
}

form.append(iter.key, fileData, iter.value.options);
form.append(iter.key, fileData, {
...createFormDataOptions(iter.value.options?.headers || {}),
filename: iter.value.options?.filename,
});
} else if (isFormDataWrapper(iter.value)) {
form.append(
iter.key,
iter.value.data,
createFormDataOptions(iter.value.headers || {})
);
} else {
form.append(iter.key, iter.value);
}
Expand Down Expand Up @@ -150,7 +161,6 @@ export class HttpClient {
newRequest.headers = headers;

this.setProxyAgent(newRequest);

return newRequest;
}

Expand Down Expand Up @@ -276,3 +286,21 @@ export function isBlob(value: unknown): value is Blob {
Object.prototype.toString.call(value) === '[object Blob]'
);
}

export function createFormDataOptions(
headers: Record<string, string>
): FormData.AppendOptions {
const headerKey = lookupCaseInsensitive(headers, 'content-type');
if (!headerKey) {
return {
header: headers,
};
}

const contentType = headers[headerKey];
delete headers[headerKey];
return {
contentType,
header: headers,
};
}
155 changes: 154 additions & 1 deletion packages/axios-client-adapter/test/httpClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { DEFAULT_TIMEOUT, HttpClient, isBlob } from '../src/httpClient';
import {
DEFAULT_TIMEOUT,
HttpClient,
isBlob,
createFormDataOptions,
} from '../src/httpClient';
import {
AxiosHeaders,
AxiosRequestConfig,
Expand All @@ -14,6 +19,7 @@ import {
HttpResponse,
} from '@apimatic/core-interfaces';
import { FileWrapper } from '@apimatic/file-wrapper';
import { createFormData } from '@apimatic/core-interfaces';
import FormData from 'form-data';
import fs from 'fs';

Expand Down Expand Up @@ -138,6 +144,102 @@ describe('HTTP Client', () => {
});
});

it('converts request with http form-data containing FormDataWrapper and FileWrapper', async () => {
const httpClient = new HttpClient(AbortError);
// Need FileWrapper to trigger multipart mode
const fileWrapper = new FileWrapper(
fs.createReadStream('test/dummy_file.txt'),
{
filename: 'document.txt',
headers: { 'content-type': 'text/plain' },
}
);
const formDataWrapper = createFormData(
{ userId: 123, name: 'Test User' },
{ 'content-type': 'application/json' }
);
const formDataBody: HttpRequestMultipartFormBody = {
type: 'form-data',
content: [
{ key: 'file', value: fileWrapper },
{ key: 'metadata', value: formDataWrapper! },
{ key: 'param1', value: 'value1' },
],
};

const request: HttpRequest = {
method: 'POST',
url: 'http://apimatic.hopto.org:3000/test/requestBuilder',
headers: { 'test-header': 'test-value' },
body: formDataBody,
responseType: 'text',
};

const axiosRequestConfig = httpClient.convertHttpRequest(request);
expect(axiosRequestConfig).toMatchObject({
url: 'http://apimatic.hopto.org:3000/test/requestBuilder',
method: 'POST',
headers: {
'test-header': 'test-value',
'content-type': new RegExp(
'^multipart/form-data; boundary=--------------------------'
),
},
timeout: DEFAULT_TIMEOUT,
responseType: 'text',
data: expect.any(FormData),
});
});

it('converts request with mixed form-data containing multiple FormDataWrappers', async () => {
const httpClient = new HttpClient(AbortError);
// Need FileWrapper to trigger multipart mode
const fileWrapper = new FileWrapper(
fs.createReadStream('test/dummy_file.txt')
);
const formDataWrapper1 = createFormData(
{ type: 'document', status: 'active' },
{ 'content-type': 'application/json' }
);
const formDataWrapper2 = createFormData({
version: 1,
lastModified: '2025-10-31',
});

const formDataBody: HttpRequestMultipartFormBody = {
type: 'form-data',
content: [
{ key: 'file', value: fileWrapper },
{ key: 'metadata', value: formDataWrapper1! },
{ key: 'version_info', value: formDataWrapper2! },
{ key: 'description', value: 'Test upload' },
],
};

const request: HttpRequest = {
method: 'POST',
url: 'http://apimatic.hopto.org:3000/test/upload',
headers: { authorization: 'Bearer token123' },
body: formDataBody,
responseType: 'text',
};

const axiosRequestConfig = httpClient.convertHttpRequest(request);
expect(axiosRequestConfig).toMatchObject({
url: 'http://apimatic.hopto.org:3000/test/upload',
method: 'POST',
headers: {
authorization: 'Bearer token123',
'content-type': new RegExp(
'^multipart/form-data; boundary=--------------------------'
),
},
timeout: DEFAULT_TIMEOUT,
responseType: 'text',
data: expect.any(FormData),
});
});

it('converts request with http stream body(file stream) and http get method', async () => {
const httpClient = new HttpClient(AbortError);
const streamBody: HttpRequestStreamBody = {
Expand Down Expand Up @@ -312,6 +414,57 @@ describe('HTTP Client', () => {
});
});

describe('createFormDataOptions', () => {
it('should return headers with content type from formDataWrapper headers', () => {
const formDataWrapper = createFormData(
{ key: 'value' },
{ 'content-type': 'application/json' }
);

const result = createFormDataOptions(formDataWrapper?.headers || {});

expect(result).toEqual({
contentType: 'application/json',
header: {},
});
});

it('should return undefined content type when not provided in headers', () => {
const formDataWrapper = createFormData({ key: 'value' }, {});

const result = createFormDataOptions(formDataWrapper?.headers || {});

expect(result).toEqual({
contentType: undefined,
header: {},
});
});

it('should handle formDataWrapper with no headers', () => {
const formDataWrapper = createFormData({ key: 'value' });

const result = createFormDataOptions(formDataWrapper?.headers || {});

expect(result).toEqual({
header: {},
});
});

it('should handle case-insensitive content-type header lookup', () => {
const formDataWrapper = createFormData(
{ test: 'data' },
{ 'Content-Type': 'application/json; charset=utf-8' }
);

const result = createFormDataOptions(formDataWrapper?.headers || {});

expect(result).toEqual({
contentType: 'application/json; charset=utf-8',
header: {},
});
});
});

describe('test blob type', () => {
test.each([
[
Expand Down
6 changes: 1 addition & 5 deletions packages/convert-to-stream/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

### [0.1.8-alpha.1](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/convert-to-stream@0.1.7...@apimatic/convert-to-stream@0.1.8-alpha.1) (2025-09-29)

**Note:** Version bump only for package @apimatic/convert-to-stream

### [0.1.8-alpha.0](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/convert-to-stream@0.1.7...@apimatic/convert-to-stream@0.1.8-alpha.0) (2025-09-29)
### [0.1.8](https://github.com/apimatic/apimatic-js-runtime/compare/@apimatic/convert-to-stream@0.1.7...@apimatic/convert-to-stream@0.1.8) (2025-09-30)

**Note:** Version bump only for package @apimatic/convert-to-stream

Expand Down
Loading
Loading