fix: run error hook when provider returns reason error or error code (#926)
## This PR - runs error hook when provider returns reason error or error code ### Related Issues Fixes #925 ### Notes Based on a conversation in Slack: https://cloud-native.slack.com/archives/C06E4DE6S07/p1714581197391509 --------- Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
This commit is contained in:
parent
f0de66770b
commit
c6d0b5da9c
|
|
@ -175,13 +175,13 @@ export default {
|
|||
displayName: 'react',
|
||||
testEnvironment: 'jsdom',
|
||||
preset: 'ts-jest',
|
||||
testMatch: ['<rootDir>/packages/react/test/**/*.spec.ts*'],
|
||||
testMatch: ['<rootDir>/packages/react/test/**/*.spec.{ts,tsx}'],
|
||||
moduleNameMapper: {
|
||||
'@openfeature/core': '<rootDir>/packages/shared/src',
|
||||
'@openfeature/web-sdk': '<rootDir>/packages/client/src',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx$': [
|
||||
'^.+\\.(ts|tsx)$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/packages/react/test/tsconfig.json',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ import {
|
|||
ResolutionDetails,
|
||||
SafeLogger,
|
||||
StandardResolutionReasons,
|
||||
statusMatchesEvent
|
||||
instantiateErrorByErrorCode,
|
||||
statusMatchesEvent,
|
||||
} from '@openfeature/core';
|
||||
import { FlagEvaluationOptions } from '../evaluation';
|
||||
import { ProviderEvents } from '../events';
|
||||
|
|
@ -225,6 +226,10 @@ export class OpenFeatureClient implements Client {
|
|||
flagKey,
|
||||
};
|
||||
|
||||
if (evaluationDetails.errorCode) {
|
||||
throw instantiateErrorByErrorCode(evaluationDetails.errorCode);
|
||||
}
|
||||
|
||||
this.afterHooks(allHooksReversed, hookContext, evaluationDetails, options);
|
||||
|
||||
return evaluationDetails;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import {
|
|||
GeneralError,
|
||||
OpenFeature,
|
||||
Hook,
|
||||
StandardResolutionReasons,
|
||||
ErrorCode,
|
||||
} from '../src';
|
||||
|
||||
const BOOLEAN_VALUE = true;
|
||||
|
|
@ -206,6 +208,27 @@ describe('Hooks', () => {
|
|||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('"error" must run if resolution details contains an error code', () => {
|
||||
(MOCK_ERROR_PROVIDER.resolveBooleanEvaluation as jest.Mock).mockReturnValue({
|
||||
value: BOOLEAN_VALUE,
|
||||
errorCode: ErrorCode.FLAG_NOT_FOUND,
|
||||
});
|
||||
|
||||
const mockErrorHook = jest.fn();
|
||||
|
||||
const details = client.getBooleanDetails(FLAG_KEY, false, {
|
||||
hooks: [{ error: mockErrorHook }],
|
||||
});
|
||||
|
||||
expect(mockErrorHook).toHaveBeenCalled();
|
||||
expect(details).toEqual(
|
||||
expect.objectContaining({
|
||||
errorCode: ErrorCode.FLAG_NOT_FOUND,
|
||||
reason: StandardResolutionReasons.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
ResolutionDetails,
|
||||
SafeLogger,
|
||||
StandardResolutionReasons,
|
||||
instantiateErrorByErrorCode,
|
||||
statusMatchesEvent,
|
||||
} from '@openfeature/core';
|
||||
import { FlagEvaluationOptions } from '../evaluation';
|
||||
|
|
@ -278,6 +279,10 @@ export class OpenFeatureClient implements Client, ManageContext<OpenFeatureClien
|
|||
flagKey,
|
||||
};
|
||||
|
||||
if (evaluationDetails.errorCode) {
|
||||
throw instantiateErrorByErrorCode(evaluationDetails.errorCode);
|
||||
}
|
||||
|
||||
await this.afterHooks(allHooksReversed, hookContext, evaluationDetails, options);
|
||||
|
||||
return evaluationDetails;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import { OpenFeature, Provider, ResolutionDetails, Client, FlagValueType, EvaluationContext, Hook } from '../src';
|
||||
import {
|
||||
OpenFeature,
|
||||
Provider,
|
||||
ResolutionDetails,
|
||||
Client,
|
||||
FlagValueType,
|
||||
EvaluationContext,
|
||||
Hook,
|
||||
StandardResolutionReasons,
|
||||
ErrorCode,
|
||||
} from '../src';
|
||||
|
||||
const BOOLEAN_VALUE = true;
|
||||
|
||||
const BOOLEAN_VARIANT = `${BOOLEAN_VALUE}`;
|
||||
const REASON = 'mocked-value';
|
||||
const ERROR_REASON = 'error';
|
||||
const ERROR_CODE = 'MOCKED_ERROR';
|
||||
|
||||
// a mock provider with some jest spies
|
||||
const MOCK_PROVIDER: Provider = {
|
||||
|
|
@ -28,8 +36,8 @@ const MOCK_ERROR_PROVIDER: Provider = {
|
|||
},
|
||||
resolveBooleanEvaluation: jest.fn((): Promise<ResolutionDetails<boolean>> => {
|
||||
return Promise.reject({
|
||||
reason: ERROR_REASON,
|
||||
errorCode: ERROR_CODE,
|
||||
reason: StandardResolutionReasons.ERROR,
|
||||
errorCode: ErrorCode.GENERAL,
|
||||
});
|
||||
}),
|
||||
} as unknown as Provider;
|
||||
|
|
@ -357,6 +365,27 @@ describe('Hooks', () => {
|
|||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('"error" must run if resolution details contains an error code', async () => {
|
||||
(MOCK_ERROR_PROVIDER.resolveBooleanEvaluation as jest.Mock).mockResolvedValueOnce({
|
||||
value: BOOLEAN_VALUE,
|
||||
errorCode: ErrorCode.FLAG_NOT_FOUND,
|
||||
});
|
||||
|
||||
const mockErrorHook = jest.fn();
|
||||
|
||||
const details = await client.getBooleanDetails(FLAG_KEY, false, undefined, {
|
||||
hooks: [{ error: mockErrorHook }],
|
||||
});
|
||||
|
||||
expect(mockErrorHook).toHaveBeenCalled();
|
||||
expect(details).toEqual(
|
||||
expect.objectContaining({
|
||||
errorCode: ErrorCode.FLAG_NOT_FOUND,
|
||||
reason: StandardResolutionReasons.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -636,8 +665,8 @@ describe('Hooks', () => {
|
|||
],
|
||||
resolveBooleanEvaluation: jest.fn((): Promise<ResolutionDetails<boolean>> => {
|
||||
return Promise.reject({
|
||||
reason: ERROR_REASON,
|
||||
errorCode: ERROR_CODE,
|
||||
reason: StandardResolutionReasons.ERROR,
|
||||
errorCode: ErrorCode.INVALID_CONTEXT,
|
||||
});
|
||||
}),
|
||||
} as unknown as Provider;
|
||||
|
|
@ -717,8 +746,8 @@ describe('Hooks', () => {
|
|||
],
|
||||
resolveBooleanEvaluation: jest.fn((): Promise<ResolutionDetails<boolean>> => {
|
||||
return Promise.reject({
|
||||
reason: ERROR_REASON,
|
||||
errorCode: ERROR_CODE,
|
||||
reason: StandardResolutionReasons.ERROR,
|
||||
errorCode: ErrorCode.PROVIDER_NOT_READY,
|
||||
});
|
||||
}),
|
||||
} as unknown as Provider;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,45 @@
|
|||
export * from './general-error';
|
||||
export * from './flag-not-found-error';
|
||||
export * from './parse-error';
|
||||
export * from './type-mismatch-error';
|
||||
export * from './targeting-key-missing-error';
|
||||
export * from './invalid-context-error';
|
||||
export * from './open-feature-error-abstract';
|
||||
export * from './provider-not-ready-error';
|
||||
export * from './provider-fatal-error';
|
||||
import { ErrorCode } from '../evaluation';
|
||||
|
||||
import { FlagNotFoundError } from './flag-not-found-error';
|
||||
import { GeneralError } from './general-error';
|
||||
import { InvalidContextError } from './invalid-context-error';
|
||||
import { OpenFeatureError } from './open-feature-error-abstract';
|
||||
import { ParseError } from './parse-error';
|
||||
import { ProviderFatalError } from './provider-fatal-error';
|
||||
import { ProviderNotReadyError } from './provider-not-ready-error';
|
||||
import { TargetingKeyMissingError } from './targeting-key-missing-error';
|
||||
import { TypeMismatchError } from './type-mismatch-error';
|
||||
|
||||
const instantiateErrorByErrorCode = (errorCode: ErrorCode, message?: string): OpenFeatureError => {
|
||||
switch (errorCode) {
|
||||
case ErrorCode.FLAG_NOT_FOUND:
|
||||
return new FlagNotFoundError(message);
|
||||
case ErrorCode.PARSE_ERROR:
|
||||
return new ParseError(message);
|
||||
case ErrorCode.TYPE_MISMATCH:
|
||||
return new TypeMismatchError(message);
|
||||
case ErrorCode.TARGETING_KEY_MISSING:
|
||||
return new TargetingKeyMissingError(message);
|
||||
case ErrorCode.INVALID_CONTEXT:
|
||||
return new InvalidContextError(message);
|
||||
case ErrorCode.PROVIDER_NOT_READY:
|
||||
return new ProviderNotReadyError(message);
|
||||
case ErrorCode.PROVIDER_FATAL:
|
||||
return new ProviderFatalError(message);
|
||||
default:
|
||||
return new GeneralError(message);
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
FlagNotFoundError,
|
||||
GeneralError,
|
||||
InvalidContextError,
|
||||
ParseError,
|
||||
ProviderFatalError,
|
||||
ProviderNotReadyError,
|
||||
TargetingKeyMissingError,
|
||||
TypeMismatchError,
|
||||
OpenFeatureError,
|
||||
instantiateErrorByErrorCode,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue