Skip to content

Commit

Permalink
feat(square): add support create square factory with custom square cl…
Browse files Browse the repository at this point in the history
…ient
  • Loading branch information
tudormd committed Feb 24, 2023
1 parent cce4d2d commit fcb7a84
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 19 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@goparrot/square-connect-plus",
"description": "Extends the official Square Node.js SDK library with additional functionality",
"version": "1.6.1",
"version": "1.6.2-dev-11393.10",
"author": "Coroliov Oleg",
"license": "MIT",
"private": false,
Expand Down
21 changes: 4 additions & 17 deletions src/client/SquareClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
CatalogApi,
CheckoutApi,
EmployeesApi,
Error as SquareError,
GiftCardActivitiesApi,
GiftCardsApi,
InventoryApi,
Expand All @@ -24,14 +23,13 @@ import type {
} from 'square';
import { Client, DEFAULT_CONFIGURATION } from 'square';
import { v4 as uuidv4 } from 'uuid';
import type { BaseApi } from 'square/dist/api/baseApi';
import type { FunctionKeys } from 'utility-types';
import { retryableErrorCodes } from '../constants';
import type { BaseApi } from 'square/dist/api/baseApi';
import { SquareApiException } from '../exception';
import type { ISquareClientConfig, ISquareClientDefaultConfig, ISquareClientMergedConfig } from '../interface';
import type { ILogger } from '../logger';
import { NullLogger } from '../logger';
import { exponentialDelay, mergeDeepProps, sleep } from '../utils';
import { exponentialDelay, isRetryableSquareApiException, mergeDeepProps, sleep } from '../utils';
import { CustomerClientApi } from './CustomerClientApi';

type ApiName = {
Expand Down Expand Up @@ -247,7 +245,7 @@ export class SquareClient {
/**
* @throws SquareApiException
*/
private proxy<T extends ApiName>(apiName: T, retryableMethods: FunctionKeys<Client[T]>[]): Client[T] {
protected proxy<T extends ApiName>(apiName: T, retryableMethods: FunctionKeys<Client[T]>[]): Client[T] {
const api = this.getOriginClient()[apiName];

return this.proxyWithInstance(apiName, api, retryableMethods);
Expand Down Expand Up @@ -317,23 +315,12 @@ export class SquareClient {
return retry();
}

private isRetryableException(error: SquareApiException): boolean {
const squareError: SquareError | undefined = error.errors?.[0];
const isRetryableResponseStatusCode =
[408, 429].includes(error.statusCode) || (error.statusCode >= 500 && error.statusCode <= 599 && error.statusCode !== 501);
if (squareError) {
return retryableErrorCodes.includes(squareError.code);
}

return isRetryableResponseStatusCode;
}

private retryCondition: (error: SquareApiException, maxRetries: number, retries: number) => Promise<boolean> = async (
error: SquareApiException,
maxRetries: number,
retries: number,
) => {
if (this.isRetryableException(error) && maxRetries > retries) {
if (isRetryableSquareApiException(error) && maxRetries > retries) {
return true;
}

Expand Down
12 changes: 12 additions & 0 deletions src/client/SquareClientFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { ISquareClientConfig } from '../interface';
import { SquareClient } from './SquareClient';

export type ClassConstructor<T> = {
new (...args: any[]): T;
};

export class SquareClientFactory {
static create(accessToken: string, config: ISquareClientConfig = {}): SquareClient {
return new SquareClient(accessToken, config);
Expand All @@ -9,4 +13,12 @@ export class SquareClientFactory {
create(accessToken: string, config: ISquareClientConfig = {}): SquareClient {
return SquareClientFactory.create(accessToken, config);
}

static createCustomSquareClient<T extends SquareClient>(client: ClassConstructor<T>, accessToken: string, config: ISquareClientConfig = {}): T {
return new client(accessToken, config);
}

createCustomSquareClient<T extends SquareClient>(client: ClassConstructor<T>, accessToken: string, config: ISquareClientConfig = {}): T {
return SquareClientFactory.createCustomSquareClient(client, accessToken, config);
}
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './retry.utils';
export * from './SquareDataMapper';
export * from './helper';
export * from './isSquareApiException';
export * from './isRetryableSquareApiException';
13 changes: 13 additions & 0 deletions src/utils/isRetryableSquareApiException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Error as SquareError } from 'square';
import type { SquareApiException } from '../exception';
import { retryableErrorCodes } from '../constants';

export function isRetryableSquareApiException(error: SquareApiException): boolean {
const squareError: SquareError | undefined = error.errors?.[0];

if (squareError) {
return retryableErrorCodes.includes(squareError.code);
}

return [408, 429].includes(error.statusCode) || (error.statusCode >= 500 && error.statusCode <= 599 && error.statusCode !== 501);
}
2 changes: 1 addition & 1 deletion test/integration/client/square-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('SquareClient (integration)', (): void => {
it('should retrieve loyalty program', async (): Promise<unknown> => {
return new SquareClient(accessToken, config)
.getLoyaltyApi()
.retrieveLoyaltyProgram('main')
.retrieveLoyaltyProgram('main_not_found')
.should.eventually.be.rejectedWith(Error, /^Merchant does not have a loyalty program/);
});
});
Expand Down
22 changes: 22 additions & 0 deletions test/unit/client/SquareClientFactory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ describe('SquareClientFactory (unit)', (): void => {
});
});

describe('#createCustomSquareClient', () => {
it('should be statically init with Test class instance and accessToken (default config)', (): void => {
class Test extends SquareClient {}
SquareClientFactory.createCustomSquareClient(Test, accessToken).should.be.instanceOf(Test);
});

it('should be init with Test class instance and accessToken only (default config)', (): void => {
class Test extends SquareClient {}
new SquareClientFactory().createCustomSquareClient(Test, accessToken).should.be.instanceOf(Test);
});

it('should be statically init with with Test class instance and accessToken and config', (): void => {
class Test extends SquareClient {}
SquareClientFactory.createCustomSquareClient(Test, accessToken, config).should.be.instanceOf(Test);
});

it('should be init with with with Test class instance and accessToken and config', (): void => {
class Test extends SquareClient {}
new SquareClientFactory().createCustomSquareClient(Test, accessToken, config).should.be.instanceOf(Test);
});
});

describe('#generateIdempotencyKey', (): void => {
it('should return string', (): void => {
SquareClient.generateIdempotencyKey()
Expand Down

0 comments on commit fcb7a84

Please sign in to comment.