Skip to content

Commit 2a000c2

Browse files
committed
implement logger and add test cases
1 parent 5a6da9b commit 2a000c2

File tree

5 files changed

+200
-16
lines changed

5 files changed

+200
-16
lines changed

packages/core/src/lib/loggers/httpLogger.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,55 @@ import {
66
VulcanExtensionId,
77
VulcanInternalExtension,
88
} from '../../models/extensions';
9-
import axios from 'axios';
9+
import axios, { AxiosRequestHeaders } from 'axios';
10+
import { ConnectionConfig } from '../utils/url';
1011

11-
interface HttpLoggerConfig {
12+
export interface HttpLoggerConfig {
1213
connection?: HttpLoggerConnectionConfig;
1314
}
1415

15-
interface HttpLoggerConnectionConfig {
16-
protocol?: string | undefined;
17-
host?: string | undefined;
18-
port?: number | string;
19-
path?: string | undefined;
20-
headers?: NodeJS.Dict<string | string[]> | undefined;
16+
export interface HttpLoggerConnectionConfig extends ConnectionConfig {
17+
headers?: Record<string, string | number | boolean> | undefined;
2118
}
2219

2320
@VulcanInternalExtension('activity-log')
2421
@VulcanExtensionId(ActivityLoggerType.HTTP_LOGGER)
2522
export class HttpLogger extends BaseActivityLogger<HttpLoggerConfig> {
23+
private logger = this.getLogger();
24+
2625
public async log(payload: any): Promise<void> {
26+
if (!this.isEnabled()) return;
2727
const option = this.getOptions();
28-
if (!option) {
29-
throw new Error('Http logger option is not defined.');
28+
if (!option?.connection) {
29+
throw new Error('Http logger connection should be provided');
3030
}
31-
// TODO-ac: should implement http logger
31+
const headers = option.connection.headers;
32+
const url = this.getUrl(option.connection);
3233
try {
3334
// get connection info from option and use axios to send a post requet to the endpoint
34-
const { protocol, host, port, path, headers } = option.connection!;
35-
const url = `${protocol}://${host}:${port}${path}`;
36-
await axios.post(url, payload, { headers: headers as any });
35+
await this.sendActivityLog(url, payload, headers);
3736
} catch (err) {
38-
console.error(err);
37+
this.logger.debug(
38+
`Failed to send activity log to http logger, url: ${url}`
39+
);
40+
throw err;
3941
}
4042
}
43+
44+
protected sendActivityLog = async (
45+
url: string,
46+
payload: JSON,
47+
headers: AxiosRequestHeaders | undefined
48+
): Promise<void> => {
49+
await axios.post(url, payload, {
50+
headers: headers,
51+
});
52+
};
53+
54+
protected getUrl = (connection: HttpLoggerConnectionConfig): string => {
55+
const { ssl, host, port, path = '' } = connection;
56+
const protocol = ssl ? 'https' : 'http';
57+
const urlbase = `${protocol}://${host}:${port}`;
58+
return new URL(path, urlbase).href;
59+
};
4160
}

packages/core/src/lib/utils/url.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export interface ConnectionConfig {
2+
ssl?: boolean | undefined;
3+
host?: string | undefined;
4+
port?: number | string;
5+
path?: string | undefined;
6+
}
7+
8+
export const getUrl = (connection: ConnectionConfig): string => {
9+
const { ssl, host, port, path = '' } = connection;
10+
const protocol = ssl ? 'https' : 'http';
11+
let urlbase = `${protocol}://${host}`;
12+
urlbase = port ? `${urlbase}:${port}` : urlbase;
13+
return new URL(path, urlbase).href;
14+
};

packages/core/src/models/extensions/logger.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,20 @@ export abstract class BaseActivityLogger<ActivityLoggerTypeOption>
1717
{
1818
public abstract log(context: any): Promise<void>;
1919

20+
protected isEnabled(): boolean {
21+
const config = this.getConfig();
22+
if (!config) return false;
23+
if (config.enabled === true) return true;
24+
else return false;
25+
}
26+
2027
protected getOptions(): ActivityLoggerTypeOption | undefined {
2128
if (!this.getConfig()) return undefined;
2229
if (!this.getConfig()['options']) return undefined;
2330
const option = this.getConfig()['options'][
2431
this.getExtensionId()!
2532
] as ActivityLoggerTypeOption;
26-
33+
console.log('option', option);
2734
return option;
2835
}
2936
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import sinon from 'ts-sinon';
2+
import { HttpLogger } from '../src/lib/loggers/httpLogger';
3+
class MockHttpLogger extends HttpLogger {
4+
public override sendActivityLog = jest.fn();
5+
}
6+
const createMockHttpLogger = (config: any) => {
7+
return new MockHttpLogger(config, 'httpLogger');
8+
};
9+
describe('Activity logs', () => {
10+
it('should throw error when logger is enabled but connection is not provided', async () => {
11+
const config = {
12+
enabled: true,
13+
options: {
14+
'http-logger': {
15+
connection: undefined,
16+
},
17+
},
18+
};
19+
20+
const httpLogger = createMockHttpLogger(config);
21+
22+
await expect(httpLogger.log({})).rejects.toThrow(
23+
'Http logger connection should be provided'
24+
);
25+
});
26+
27+
it('should not throw error when logger is disabled', async () => {
28+
const config = {
29+
enabled: false,
30+
};
31+
32+
const httpLogger = createMockHttpLogger(config);
33+
34+
await expect(httpLogger.log({})).resolves.not.toThrow();
35+
});
36+
37+
// should not throw error when logger is enabled and connection is provided
38+
it('should not throw error when logger is enabled and connection is provided', async () => {
39+
const config = {
40+
enabled: true,
41+
options: {
42+
'http-logger': {
43+
connection: {
44+
ssl: true,
45+
host: 'localhost',
46+
port: 8080,
47+
path: '/test',
48+
},
49+
},
50+
},
51+
};
52+
const httpLogger = createMockHttpLogger(config);
53+
sinon.stub(httpLogger, 'sendActivityLog').resolves();
54+
await expect(httpLogger.log({})).resolves.not.toThrow();
55+
});
56+
57+
// should throw error when logger is enabled and connection is provided but request fails
58+
it('should throw error when logger is enabled and connection is provided but request fails', async () => {
59+
const config = {
60+
enabled: true,
61+
options: {
62+
'http-logger': {
63+
connection: {
64+
ssl: true,
65+
host: 'localhost',
66+
port: 8080,
67+
path: '/test',
68+
},
69+
},
70+
},
71+
};
72+
// stub sendActivityLog to throw error
73+
const httpLogger = createMockHttpLogger(config);
74+
sinon.stub(httpLogger, 'sendActivityLog').throws();
75+
await expect(httpLogger.log({})).rejects.toThrow();
76+
});
77+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { get } from 'lodash';
2+
import { getUrl, ConnectionConfig } from '../../src/lib/utils/url';
3+
4+
describe('url util functions', () => {
5+
it('should return url if all connection properties were set', () => {
6+
const connection = {
7+
ssl: true,
8+
host: 'localhost',
9+
port: 8080,
10+
path: '/test',
11+
} as ConnectionConfig;
12+
13+
const url = getUrl(connection);
14+
expect(url).toBe('https://localhost:8080/test');
15+
});
16+
17+
it('should return url if ssl or path is not set', () => {
18+
const connection = {
19+
host: 'localhost',
20+
} as ConnectionConfig;
21+
22+
const url = getUrl(connection);
23+
expect(url).toBe('http://localhost/');
24+
});
25+
26+
it('should return url if host was an IP address', () => {
27+
const connection = {
28+
ssl: false,
29+
host: '127.0.0.1',
30+
port: 8080,
31+
path: '/test',
32+
} as ConnectionConfig;
33+
const url = getUrl(connection);
34+
expect(url).toBe('http://127.0.0.1:8080/test');
35+
});
36+
37+
it.each([
38+
{
39+
ssl: false,
40+
host: 'localhost',
41+
port: 8080,
42+
path: '/test',
43+
},
44+
{
45+
host: 'localhost',
46+
port: 8080,
47+
path: '/test',
48+
},
49+
])(
50+
'should use protocal http if ssl was not set or set to false',
51+
(connection) => {
52+
const url = getUrl(connection);
53+
expect(url).toBe('http://localhost:8080/test');
54+
}
55+
);
56+
57+
it('should return url if host was a DNS name and port was not set', () => {
58+
const connection = {
59+
ssl: true,
60+
host: 'DNSName',
61+
path: '/test',
62+
} as ConnectionConfig;
63+
64+
const url = getUrl(connection);
65+
expect(url).toBe('https://dnsname/test');
66+
});
67+
});

0 commit comments

Comments
 (0)