Skip to content

Commit

Permalink
Add error logging
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospassos committed May 6, 2024
1 parent ea42e64 commit bf31ef7
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 37 deletions.
32 changes: 19 additions & 13 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
"dependencies": {
"@croct/json": "^2.0.1",
"@croct/sdk": "^0.14.0",
"@croct/sdk": "^0.15.2",
"tslib": "^2.2.0"
},
"devDependencies": {
Expand Down
22 changes: 19 additions & 3 deletions src/api/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import {Evaluator, EvaluationOptions as BaseOptions} from '@croct/sdk/evaluator';
import type {ApiKey} from '@croct/sdk/apiKey';
import type {Logger} from '@croct/sdk/logging';
import {formatCause} from '@croct/sdk/error';
import {JsonValue} from '../sdk/json';

export type EvaluationOptions<T extends JsonValue = JsonValue> = BaseOptions & AuthOptions & FetchingOptions<T>;

type FetchingOptions<T extends JsonValue> = {
baseEndpointUrl?: string,
fallback?: T,
logger?: Logger,
};

type AuthOptions = ServerSideAuthOptions | ClientSideAuthOptions;

type ServerSideAuthOptions = {
apiKey: string,
apiKey: string|ApiKey,
appId?: never,
};

Expand All @@ -21,13 +25,25 @@ type ClientSideAuthOptions = {
};

export function evaluate<T extends JsonValue>(query: string, options: EvaluationOptions<T>): Promise<T> {
const {baseEndpointUrl, fallback, apiKey, appId, ...evaluation} = options;
const {baseEndpointUrl, fallback, apiKey, appId, logger, ...evaluation} = options;
const auth: AuthOptions = apiKey !== undefined ? {apiKey: apiKey} : {appId: appId};
const promise = (new Evaluator({...auth, baseEndpointUrl: baseEndpointUrl}))
.evaluate(query, evaluation) as Promise<T>;

if (fallback !== undefined) {
return promise.catch(() => fallback);
return promise.catch(
error => {
if (logger !== undefined) {
const reference = query.length > 20
? `${query.slice(0, 20)}...`
: query;

logger.error(`Failed to evaluate query "${reference}": ${formatCause(error)}`);
}

return fallback;
},
);
}

return promise;
Expand Down
18 changes: 13 additions & 5 deletions src/api/fetchContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ import {
DynamicContentOptions as BaseDynamicOptions,
StaticContentOptions as BaseStaticOptions,
} from '@croct/sdk/contentFetcher';
import type {ApiKey} from '@croct/sdk/apiKey';
import type {Logger} from '@croct/sdk/logging';
import {formatCause} from '@croct/sdk/error';
import {JsonObject, JsonValue} from '../sdk/json';
import {FetchResponse} from '../plug';
import {SlotContent, VersionedSlotId} from '../slot';

type FetchingOptions<T extends JsonValue> = {
baseEndpointUrl?: string,
fallback?: T,
logger?: Logger,
};

type AuthOptions = ServerSideAuthOptions | ClientSideAuthOptions;

type ServerSideAuthOptions = {
apiKey: string,
apiKey: string|ApiKey,
appId?: never,
};

Expand All @@ -36,7 +40,7 @@ export function fetchContent<I extends VersionedSlotId, C extends JsonObject>(
slotId: I,
options?: FetchOptions<SlotContent<I, C>>,
): Promise<Omit<FetchResponse<I, C>, 'payload'>> {
const {apiKey, appId, fallback, baseEndpointUrl, ...fetchOptions} = options ?? {};
const {apiKey, appId, fallback, baseEndpointUrl, logger, ...fetchOptions} = options ?? {};
const auth = {appId: appId, apiKey: apiKey};
const [id, version = 'latest'] = slotId.split('@') as [I, `${number}` | 'latest' | undefined];

Expand All @@ -50,9 +54,13 @@ export function fetchContent<I extends VersionedSlotId, C extends JsonObject>(

if (fallback !== undefined) {
return promise.catch(
() => ({
content: fallback,
}),
error => {
if (logger !== undefined) {
logger.error(`Failed to fetch content for slot "${id}@${version}": ${formatCause(error)}`);
}

return {content: fallback};
},
);
}

Expand Down
19 changes: 16 additions & 3 deletions src/plug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,18 @@ export class GlobalPlug implements Plug {
.track(type, payload);
}

public evaluate<T extends JsonValue>(expression: string, options: EvaluationOptions = {}): Promise<T> {
public evaluate<T extends JsonValue>(query: string, options: EvaluationOptions = {}): Promise<T> {
return this.sdk
.evaluator
.evaluate(expression, options) as Promise<T>;
.evaluate(query, options)
.catch(error => {
const logger = this.sdk.getLogger();
const reference = query.length > 20 ? `${query.slice(0, 20)}...` : query;

logger.error(`Failed to evaluate query "${reference}": ${formatCause(error)}`);

throw error;
}) as Promise<T>;
}

public test(expression: string, options: EvaluationOptions = {}): Promise<boolean> {
Expand Down Expand Up @@ -391,7 +399,12 @@ export class GlobalPlug implements Plug {
},
content: response.content,
}),
);
)
.catch(error => {
logger.error(`Failed to fetch content for slot "${id}@${version}": ${formatCause(error)}`);

throw error;
});
},
);
}
Expand Down
23 changes: 17 additions & 6 deletions test/api/evaluate.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Evaluator} from '@croct/sdk/evaluator';
import {Logger} from '@croct/sdk/logging';
import {evaluate, EvaluationOptions} from '../../src/api';

const mockEvaluate: Evaluator['evaluate'] = jest.fn();
Expand All @@ -8,10 +9,10 @@ jest.mock(
() => ({
__esModule: true,
/*
* eslint-disable-next-line prefer-arrow-callback --
* The mock can't be an arrow function because calling new on
* an arrow function is not allowed in JavaScript.
*/
* eslint-disable-next-line prefer-arrow-callback --
* The mock can't be an arrow function because calling new on
* an arrow function is not allowed in JavaScript.
*/
Evaluator: jest.fn(function constructor(this: Evaluator) {
this.evaluate = mockEvaluate;
}),
Expand Down Expand Up @@ -73,13 +74,23 @@ describe('evaluate', () => {
});

it('should return the fallback value on error', async () => {
const logger: Logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};

const options: EvaluationOptions = {
apiKey: apiKey,
fallback: false,
logger: logger,
};

jest.mocked(mockEvaluate).mockRejectedValue(new Error('error'));
jest.mocked(mockEvaluate).mockRejectedValue(new Error('Reason'));

await expect(evaluate('"this is a long query"', options)).resolves.toBe(false);

await expect(evaluate('true', options)).resolves.toBe(false);
expect(logger.error).toHaveBeenCalledWith('Failed to evaluate query ""this is a long quer...": reason');
});
});
12 changes: 11 additions & 1 deletion test/api/fetchContent.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ContentFetcher} from '@croct/sdk/contentFetcher';
import {Logger} from '@croct/sdk/logging';
import {FetchResponse} from '../../src/plug';
import {SlotContent} from '../../src/slot';
import {fetchContent, FetchOptions} from '../../src/api';
Expand Down Expand Up @@ -185,6 +186,12 @@ describe('fetchContent', () => {

it('should return the fallback value on error', async () => {
const slotId = 'slot-id';
const logger: Logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};

const fallback: SlotContent = {
_component: 'component-id',
Expand All @@ -195,12 +202,15 @@ describe('fetchContent', () => {
apiKey: apiKey,
timeout: 100,
fallback: fallback,
logger: logger,
};

jest.mocked(mockFetch).mockRejectedValue(new Error('error'));
jest.mocked(mockFetch).mockRejectedValue(new Error('Reason'));

await expect(fetchContent(slotId, options)).resolves.toEqual({
content: fallback,
});

expect(logger.error).toHaveBeenCalledWith(`Failed to fetch content for slot "${slotId}@latest": reason`);
});
});
Loading

0 comments on commit bf31ef7

Please sign in to comment.