Skip to content
Merged
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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ npm-test:
npm test

.PHONY: test
test: npm-test example snippets
test: typecheck npm-test example snippets

.PHONY: typecheck
typecheck:
npm run typecheck

.PHONY: cover
cover:
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ This repository contains Aspose.BarCode Cloud SDK for Node.js source code.

To use these SDKs, you will need Client Id and Client Secret which can be looked up at [Aspose Cloud Dashboard](https://dashboard.aspose.cloud/applications) (free registration in Aspose Cloud is required for this).

## Requirements

- Node.js 18 or later (native `fetch` required).

## How to use the SDK

The complete source code is available in this repository folder. You can either directly use it in your project via source code or get [nmpjs distribution](https://www.npmjs.com/package/aspose-barcode-cloud-node) (recommended).
Expand Down Expand Up @@ -55,7 +59,7 @@ const config = new Barcode.Configuration(

async function generateBarcode(api) {
const request = new Barcode.GenerateRequestWrapper(
Barcode.EncodeBarcodeType.Qr,
Barcode.EncodeBarcodeType.Qr,
'Aspose.BarCode for Cloud Sample');

const oneBarcode = await api.generate(request);
Expand Down
2 changes: 1 addition & 1 deletion example.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const config = new Barcode.Configuration(

async function generateBarcode(api) {
const request = new Barcode.GenerateRequestWrapper(
Barcode.EncodeBarcodeType.Qr,
Barcode.EncodeBarcodeType.Qr,
'Aspose.BarCode for Cloud Sample');

const oneBarcode = await api.generate(request);
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@
"dist"
],
"engines": {
"node": ">=16"
"node": ">=18"
},
"scripts": {
"test": "npx jest",
"typecheck": "npx tsc --noEmit",
"cover": "npx jest --coverage",
"lint": "npx eslint src test snippets",
"format": "npx eslint src test snippets eslint.config.mjs --fix",
Expand Down
5 changes: 5 additions & 0 deletions scripts/run_example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

set -euo pipefail

if [[ "${ASPOSE_E2E:-}" != "1" ]]; then
echo "Skipping example: set ASPOSE_E2E=1 to run against live API."
exit 0
fi

TEST_DIR="demo"

rm -rf "${TEST_DIR}" || true
Expand Down
4 changes: 2 additions & 2 deletions scripts/run_snippet.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ SCRIPT_DIR=$3
CONFIG_FILE_PATH=$4
echo "Run snippet file: $FILE_PATH"

node ${SCRIPT_DIR}/insert-credentials.js $FILE_PATH $CONFIG_FILE_PATH $RUN_DIR
node "${SCRIPT_DIR}/insert-credentials.js" "$FILE_PATH" "$CONFIG_FILE_PATH" "$RUN_DIR"

node ./$RUN_DIR/"${FILE_PATH##*/}" || exit 1
node "./$RUN_DIR/${FILE_PATH##*/}" || exit 1
2 changes: 1 addition & 1 deletion scripts/run_snippets.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ popd

${SCRIPT_DIR}/run_snippet.sh "${SNIPPETS_DIR}/manualFetchToken.js" $RUN_DIR $SCRIPT_DIR $CONFIG_FILE_PATH || exit 1;

rm -rf "${RUN_DIR}" || true
rm -rf "${RUN_DIR}" || true
2 changes: 1 addition & 1 deletion snippets/generate/appearance/generateBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function generateBarcode(api, fileName) {
}
const genApi = new Barcode.GenerateApi(config);

const fileName = path.resolve('testdata', 'Code39.png');
const fileName = path.resolve('testdata', 'Code39-red.png');

generateBarcode(genApi, fileName)
.then(() => {
Expand Down
2 changes: 1 addition & 1 deletion snippets/generate/set-colorscheme/generateMultipart.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function generateBarcode(api, fileName) {
}

const genApi = new Barcode.GenerateApi(config);
const fileName = path.resolve('testdata', 'Code39.png');
const fileName = path.resolve('testdata', 'Code39-green.png');

generateBarcode(genApi, fileName)
.then(() => {
Expand Down
7 changes: 4 additions & 3 deletions src/Authentication.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HttpOptions } from './httpClient';

export interface Authentication {
/**
* Apply authentication settings to header and query params.
*/
applyToRequestAsync(requestOptions: HttpOptions): Promise<void>;
applyToRequestAsync(requestOptions: {
headers?: Record<string, string>;
qs?: Record<string, string>;
}): Promise<void>;
}
151 changes: 137 additions & 14 deletions src/JWTAuth.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
import { Configuration } from './Configuration';
import { Authentication } from './Authentication';
import { HttpClient, HttpOptions } from './httpClient';
import { ApiErrorResponse } from './models';

type AuthResponse = {
statusCode: number;
statusMessage: string;
headers: NodeJS.Dict<string | string[]>;
body: any;
};

type AuthRejectType = {
response: AuthResponse | null;
errorResponse: ApiErrorResponse | null;
error: Error;
};

export class JWTAuth implements Authentication {
private _accessToken?: string;
private readonly _configuration: Configuration;
private _client: HttpClient;
private readonly _fetcher: typeof fetch;

constructor(configuration: Configuration) {
this._configuration = configuration;
const resolvedFetch = (globalThis as { fetch?: typeof fetch }).fetch;
if (!resolvedFetch) {
throw new Error('Global fetch API is not available. Please use Node.js 18+.');
}

this._fetcher = resolvedFetch;

if (configuration.accessToken) {
// Use saved token
this._accessToken = configuration.accessToken;
}
this._client = new HttpClient();
}

/**
* Apply authentication settings to header and query params.
*/
public async applyToRequestAsync(requestOptions: HttpOptions): Promise<void> {
public async applyToRequestAsync(requestOptions: {
headers?: Record<string, string>;
qs?: Record<string, string>;
}): Promise<void> {
if (this._accessToken == null) {
this._accessToken = await this.requestToken();
}
Expand All @@ -36,18 +57,120 @@ export class JWTAuth implements Authentication {
if (!this._configuration.clientId || !this._configuration.clientSecret) {
throw new Error("Required 'clientId' or 'clientSecret' not specified in configuration.");
}
const requestOptions: HttpOptions = {
method: 'POST',
uri: this._configuration.tokenUrl,
form: {
grant_type: 'client_credentials',
client_id: this._configuration.clientId,
client_secret: this._configuration.clientSecret,
},
const requestBody = new URLSearchParams({
grant_type: 'client_credentials',
client_id: this._configuration.clientId,
client_secret: this._configuration.clientSecret,
}).toString();
let response: Response;
try {
response = await this._fetcher(this._configuration.tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: requestBody,
});
} catch (error) {
throw {
response: null,
error: this.normalizeFetchError(error),
errorResponse: null,
} as AuthRejectType;
}

const responseBody = await response.text();
const responseInfo: AuthResponse = {
statusCode: response.status,
statusMessage: response.statusText,
headers: this.toHeaderDict(response.headers),
body: responseBody,
};

const result = await this._client.requestAsync(requestOptions);
const parsed = JSON.parse(result.body);
if (!response.ok) {
const rejectObject: AuthRejectType = {
response: responseInfo,
error: new Error(
`Error on '${this._configuration.tokenUrl}': ${response.status} ${response.statusText}`
),
errorResponse: null,
};
let errorResponse = null;
try {
errorResponse = JSON.parse(responseBody) as ApiErrorResponse;
} catch (parseError) {}

if (errorResponse) {
rejectObject.errorResponse = errorResponse;
} else {
rejectObject.error.message += `. ${responseBody}`;
}
throw rejectObject;
}

const parsed = JSON.parse(responseBody);
return parsed.access_token;
}

private toHeaderDict(headers: Headers): NodeJS.Dict<string | string[]> {
const normalizedHeaders: NodeJS.Dict<string | string[]> = {};

headers.forEach((value, key) => {
const existing = normalizedHeaders[key];
if (existing === undefined) {
normalizedHeaders[key] = value;
return;
}

if (Array.isArray(existing)) {
existing.push(value);
normalizedHeaders[key] = existing;
return;
}

normalizedHeaders[key] = [existing, value];
});

return normalizedHeaders;
}

private normalizeFetchError(error: unknown): Error {
if (error instanceof Error) {
const mutableError = error as Error & { code?: string; cause?: unknown; name: string };
let normalizedCode = mutableError.code;

if (!normalizedCode) {
const cause = mutableError.cause;
if (cause && typeof cause === 'object' && 'code' in (cause as { code?: string })) {
const code = (cause as { code?: string }).code;
if (code) {
normalizedCode = String(code);
}
}
}

if (!normalizedCode) {
normalizedCode = mutableError.name || 'FETCH_ERROR';
}

try {
if (!mutableError.code) {
mutableError.code = normalizedCode;
}
} catch (assignError) {}

if (mutableError.code) {
return mutableError;
}

const wrapped = new Error(mutableError.message);
wrapped.name = mutableError.name;
(wrapped as { code?: string }).code = normalizedCode;
return wrapped;
}

const wrapped = new Error(String(error));
(wrapped as { code?: string }).code = 'FETCH_ERROR';
return wrapped;
}
}
Loading
Loading