Skip to content

Commit

Permalink
fix: missing configuration for logs
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Logs are disabled by default now.
You can enable them by adding `logs: { enabled: true }` or `logs: { useNodeEnv: true }`
  • Loading branch information
HormCodes committed Jan 6, 2022
1 parent 78a7df0 commit 35c056c
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 15 deletions.
4 changes: 2 additions & 2 deletions src/api/checkout-v3/checkout-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class CheckoutV3 extends HttpRequest {
*/
retrieveOrder(orderId: string): Promise<OrderResponse> {
if (!orderId) {
console.warn(
this.logger.warn(
'\x1b[33m',
'⚠️ Order ID is required to fetch an order',
'\x1b[0m'
Expand All @@ -31,7 +31,7 @@ export class CheckoutV3 extends HttpRequest {
*/
updateOrder(orderId: string, body: OrderBody): Promise<OrderResponse> {
if (!orderId) {
console.warn(
this.logger.warn(
'\x1b[33m',
'⚠️ Order ID is required to update an order',
'\x1b[0m'
Expand Down
6 changes: 5 additions & 1 deletion src/crystallize-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
OrderBody,
OrderLine,
} from './api/checkout-v3';
import { OptionalLogConfig, optionalLoggerFactory } from './utils';

export interface Defaults {
host_uri: string;
Expand All @@ -12,6 +13,7 @@ export interface Defaults {
locale?: string;
merchant_urls?: MerchantUrls;
shipping_options?: Array<ShippingOption>;
logs?: OptionalLogConfig;
}

interface SubscriptionPlan {
Expand Down Expand Up @@ -89,8 +91,10 @@ export class CrystallizeKlarnaHelpers {
defaults: Defaults;

constructor(defaults: Defaults) {
const logger = optionalLoggerFactory(defaults.logs);

if (!defaults?.host_uri) {
console.warn(
logger.warn(
'\x1b[33m',
'⚠️ host_uri property is mandatory while initialising CrystallizeKlarnaHelpers',
'\x1b[0m'
Expand Down
15 changes: 10 additions & 5 deletions src/http-request.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as https from 'https';
import * as http from 'http';
import { parseJSON } from './utils';
import { Logger, parseJSON } from './utils';

export interface Options {
authorization: string;
apiEndpoint: string;
logger: Logger;
}

export interface Response {
Expand All @@ -14,19 +15,23 @@ export interface Response {
}

export class HttpRequest {
authorization: string;
hostname: string;
protected readonly authorization: string;
protected readonly hostname: string;
protected readonly logger: Logger;

constructor(options: Options) {
this.authorization = options.authorization;
this.hostname = options.apiEndpoint;
this.logger = options.logger;
}

invoke(
httpMethod: string,
path: string,
requestBody = {}
): Promise<Response> {
const logger = this.logger;

return new Promise(resolve => {
const options: http.RequestOptions = {
method: httpMethod,
Expand All @@ -50,7 +55,7 @@ export class HttpRequest {

// Check if the response is really from Klarna?
if (!res?.headers?.['klarna-correlation-id']) {
console.warn(
logger.warn(
'\x1b[41m',
'⚠️ important: api response headers do not contain klarna correlation id',
'\x1b[0m'
Expand Down Expand Up @@ -83,7 +88,7 @@ export class HttpRequest {
}

req.on('error', function(error): void {
console.error('Klarna request errored: ' + error.message);
logger.error('Klarna request errored: ' + error.message);
resolve({ statusCode: 500, error });
});

Expand Down
15 changes: 8 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import { Options } from './http-request';
import { CheckoutV3 } from './api/checkout-v3';
import { CustomerTokenV1 } from './api/customer-token-v1';
import { OrderManagementV1 } from './api/order-management-v1';
export * from './crystallize-helpers';
import { optionalLoggerFactory } from './utils';
import { Config } from './interface';

interface Config {
username: string;
password: string;
apiEndpoint?: string;
}
export * from './interface';
export * from './crystallize-helpers';

export class Klarna {
checkoutV3: CheckoutV3;
Expand All @@ -19,8 +17,10 @@ export class Klarna {
let { apiEndpoint } = config;
const { username, password } = config;

const logger = optionalLoggerFactory(config.logs);

if (!apiEndpoint || apiEndpoint === '') {
console.warn(
logger.warn(
'\x1b[33m',
'⚠️ apiEndpoint field not provided while initializing library. Default API endpoint set to: https://api.playground.klarna.com [Test EU]',
'\x1b[0m'
Expand All @@ -41,6 +41,7 @@ export class Klarna {
authorization: `Basic ${Buffer.from(`${username}:${password}`).toString(
'base64'
)}`,
logger,
};

this.checkoutV3 = new CheckoutV3(options);
Expand Down
10 changes: 10 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { OptionalLogConfig } from './utils';

export { OptionalLogConfig } from './utils';

export interface Config {
username: string;
password: string;
apiEndpoint?: string;
logs?: OptionalLogConfig;
}
131 changes: 131 additions & 0 deletions src/utils/__tests__/logger.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Logger, optionalLoggerFactory } from '../logger';

describe('logger utils', () => {
describe('optionalLoggerFactory', () => {
const logMockUtils = getLogMockUtils();
const nodeEnvUtils = getNodeEnvUtils();

beforeEach(logMockUtils.setup);
afterEach(logMockUtils.teardown);

const callLogger = (logger: Logger) => {
logger.warn('warning');
logger.info('info');
logger.error('error');
};

it('should not call console log when disabled', () => {
const { logMock, warnMock, errorMock } = logMockUtils.getMocks();

const logger = optionalLoggerFactory({ enabled: false });

callLogger(logger);

expect(logMock).not.toBeCalled();
expect(warnMock).not.toBeCalled();
expect(errorMock).not.toBeCalled();
});

it('should call console log when enabled', () => {
const { logMock, warnMock, errorMock } = logMockUtils.getMocks();

const logger = optionalLoggerFactory({ enabled: true });

callLogger(logger);

expect(logMock).toBeCalledWith('info');
expect(warnMock).toBeCalledWith('warning');
expect(errorMock).toBeCalledWith('error');
});

it('should call console log when useEnv enabled and the env is not prod', () => {
const { logMock, warnMock, errorMock } = logMockUtils.getMocks();

nodeEnvUtils.setup('development');

process.env.NODE_ENV = 'development';

const logger = optionalLoggerFactory({ useNodeEnv: true });

callLogger(logger);

expect(logMock).toBeCalledWith('info');
expect(warnMock).toBeCalledWith('warning');
expect(errorMock).toBeCalledWith('error');

nodeEnvUtils.teardown();
});

it('should not call console log when useEnv enabled and the env is prod', () => {
const { logMock, warnMock, errorMock } = logMockUtils.getMocks();

nodeEnvUtils.setup('production');

const logger = optionalLoggerFactory({ useNodeEnv: true });

callLogger(logger);

expect(logMock).not.toBeCalled();
expect(warnMock).not.toBeCalled();
expect(errorMock).not.toBeCalled();

nodeEnvUtils.teardown();
});

it('should not call console log by default', () => {
const { logMock, warnMock, errorMock } = logMockUtils.getMocks();

const logger = optionalLoggerFactory({});

callLogger(logger);

expect(logMock).not.toBeCalled();
expect(warnMock).not.toBeCalled();
expect(errorMock).not.toBeCalled();
});
});
});

function getLogMockUtils() {
let logMock: jest.SpyInstance;
let warnMock: jest.SpyInstance;
let errorMock: jest.SpyInstance;

return {
setup: () => {
logMock = jest.spyOn(console, 'log').mockImplementation(jest.fn);
warnMock = jest.spyOn(console, 'warn').mockImplementation(jest.fn);
errorMock = jest.spyOn(console, 'error').mockImplementation(jest.fn);

return {
logMock,
warnMock,
errorMock,
};
},
getMocks: () => ({
logMock,
warnMock,
errorMock,
}),
teardown: () => {
logMock.mockRestore();
warnMock.mockRestore();
errorMock.mockRestore();
},
};
}

function getNodeEnvUtils() {
let NODE_ENV: string;

return {
setup: (value: string) => {
NODE_ENV = process.env.NODE_ENV as string;
process.env.NODE_ENV = value;
},
teardown: () => {
process.env.NODE_ENV = NODE_ENV;
},
};
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { parseJSON } from './json';
export { optionalLoggerFactory, OptionalLogConfig, Logger } from './logger';
36 changes: 36 additions & 0 deletions src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function optionalLoggerFactory(
config: OptionalLogConfig = { enabled: false, useNodeEnv: false }
) {
const { enabled = false, useNodeEnv = false } = config;

const disabledLogger: Logger = {
warn: () => {},
info: () => {},
error: () => {},
};

if (useNodeEnv && process.env.NODE_ENV === 'production') {
return disabledLogger;
}

if (!enabled && !useNodeEnv) {
return disabledLogger;
}

return {
info: (...args: any) => console.log(...args),
warn: (...args: any) => console.warn(...args),
error: (...args: any) => console.error(...args),
};
}

export interface Logger {
info: (...args: any) => void;
error: (...args: any) => void;
warn: (...args: any) => void;
}

export interface OptionalLogConfig {
enabled?: boolean;
useNodeEnv?: boolean;
}
2 changes: 2 additions & 0 deletions test/http-request.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { HttpRequest } from '../src/http-request';
import nock from 'nock';
import { optionalLoggerFactory } from '../src/utils';

describe('HttpRequest', () => {
const httpRequest = new HttpRequest({
authorization: 'none',
apiEndpoint: 'example.com',
logger: optionalLoggerFactory({ enabled: false }),
});

describe('invoke', () => {
Expand Down

0 comments on commit 35c056c

Please sign in to comment.