Skip to content

Commit 32871d7

Browse files
committed
feat: updated-errors-to-general-errors
Signed-off-by: wadii <[email protected]>
1 parent 35b0f18 commit 32871d7

File tree

3 files changed

+38
-135
lines changed

3 files changed

+38
-135
lines changed

libs/providers/flagsmith/src/lib/exceptions.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

libs/providers/flagsmith/src/lib/flagsmith-provider.spec.ts

Lines changed: 29 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import FlagsmithOpenFeatureProvider from './flagsmith-provider';
2-
import {
3-
FlagNotFoundError,
4-
TypeMismatchError,
5-
Logger,
6-
StandardResolutionReasons,
7-
ProviderEvents,
8-
ProviderStatus,
9-
ErrorCode,
10-
} from '@openfeature/server-sdk';
2+
import { FlagNotFoundError, Logger, StandardResolutionReasons, ErrorCode, GeneralError } from '@openfeature/server-sdk';
113
import { Flagsmith, Flags, BaseFlag } from 'flagsmith-nodejs';
12-
import { FlagsmithProviderError } from './exceptions';
134
import { mockFlagData } from './flagsmith.mocks';
145

156
jest.mock('flagsmith-nodejs');
@@ -63,66 +54,6 @@ describe('FlagsmithOpenFeatureProvider', () => {
6354
});
6455
});
6556

66-
describe('provider status and events', () => {
67-
it('should start with NOT_READY status', () => {
68-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
69-
expect(provider.status).toBe(ProviderStatus.NOT_READY);
70-
});
71-
72-
it('should set status to READY after successful initialization', async () => {
73-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
74-
mockFlagsmith.getEnvironmentFlags.mockResolvedValue(mockFlags);
75-
76-
await provider.initialize();
77-
78-
expect(provider.status).toBe(ProviderStatus.READY);
79-
});
80-
81-
it('should set status to ERROR when initialization fails', async () => {
82-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
83-
mockFlagsmith.getEnvironmentFlags.mockRejectedValue(new Error('Connection failed'));
84-
85-
await expect(provider.initialize()).rejects.toThrow();
86-
expect(provider.status).toBe(ProviderStatus.ERROR);
87-
});
88-
89-
it('should emit ready event when status changes to READY', async () => {
90-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
91-
const readyHandler = jest.fn();
92-
provider.events.addHandler(ProviderEvents.Ready, readyHandler);
93-
94-
mockFlagsmith.getEnvironmentFlags.mockResolvedValue(mockFlags);
95-
await provider.initialize();
96-
97-
expect(readyHandler).toHaveBeenCalled();
98-
});
99-
100-
it('should emit error event when status changes to ERROR', async () => {
101-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
102-
const errorHandler = jest.fn();
103-
provider.events.addHandler(ProviderEvents.Error, errorHandler);
104-
105-
mockFlagsmith.getEnvironmentFlags.mockRejectedValue(new Error('Connection failed'));
106-
107-
try {
108-
await provider.initialize();
109-
} catch (error) {}
110-
111-
expect(errorHandler).toHaveBeenCalled();
112-
});
113-
114-
it('should set status to NOT_READY when onClose is called', async () => {
115-
const provider = new FlagsmithOpenFeatureProvider(mockFlagsmith);
116-
mockFlagsmith.getEnvironmentFlags.mockResolvedValue(mockFlags);
117-
118-
await provider.initialize();
119-
expect(provider.status).toBe(ProviderStatus.READY);
120-
121-
await provider.onClose();
122-
expect(provider.status).toBe(ProviderStatus.NOT_READY);
123-
});
124-
});
125-
12657
describe('configuration', () => {
12758
describe('useBooleanConfigValue', () => {
12859
let useBooleanConfigProvider: FlagsmithOpenFeatureProvider;
@@ -180,9 +111,16 @@ describe('FlagsmithOpenFeatureProvider', () => {
180111
useBooleanConfigValue: false,
181112
});
182113
mockFlags.getFlag.mockReturnValue(mockFlagData.booleanDefault);
183-
await expect(
184-
useFlagsmithDefaultsProvider.resolveBooleanEvaluation('default-flag', false, evaluationContext, loggerMock),
185-
).rejects.toThrow(FlagNotFoundError);
114+
const result = await useFlagsmithDefaultsProvider.resolveBooleanEvaluation(
115+
'default-flag',
116+
false,
117+
evaluationContext,
118+
loggerMock,
119+
);
120+
expect(result.value).toBe(false);
121+
expect(result.reason).toBe(StandardResolutionReasons.ERROR);
122+
expect(result.errorCode).toBe(ErrorCode.FLAG_NOT_FOUND);
123+
expect(result.errorMessage).toContain("Flag 'default-flag' was not found.");
186124
});
187125

188126
it('should throw FlagNotFoundError when flag does not exist (undefined) even with useFlagsmithDefaults true', async () => {
@@ -192,9 +130,16 @@ describe('FlagsmithOpenFeatureProvider', () => {
192130
useBooleanConfigValue: false,
193131
});
194132
mockFlags.getFlag.mockReturnValue(undefined as any);
195-
await expect(
196-
provider.resolveBooleanEvaluation('nonexistent-flag', false, evaluationContext, loggerMock),
197-
).rejects.toThrow(FlagNotFoundError);
133+
const result = await provider.resolveBooleanEvaluation(
134+
'nonexistent-flag',
135+
false,
136+
evaluationContext,
137+
loggerMock,
138+
);
139+
expect(result.value).toBe(false);
140+
expect(result.reason).toBe(StandardResolutionReasons.ERROR);
141+
expect(result.errorCode).toBe(ErrorCode.FLAG_NOT_FOUND);
142+
expect(result.errorMessage).toContain("Flag 'nonexistent-flag' was not found.");
198143
});
199144

200145
it('should return flag.value when boolean flag is default and useFlagsmithDefaults is true', async () => {
@@ -255,7 +200,7 @@ describe('FlagsmithOpenFeatureProvider', () => {
255200
mockFlags.getFlag.mockReturnValue(mockFlagData.disabledFlag);
256201
await expect(
257202
defaultProvider.resolveStringEvaluation('disabled-flag', 'test-string', evaluationContext, loggerMock),
258-
).rejects.toThrow(FlagsmithProviderError);
203+
).rejects.toThrow(GeneralError);
259204
});
260205

261206
it('should return string value when flag is disabled and returnValueForDisabledFlags is true', async () => {
@@ -429,7 +374,7 @@ describe('FlagsmithOpenFeatureProvider', () => {
429374
mockFlags.getFlag.mockReturnValue(mockFlagData.disabledFlag);
430375
await expect(
431376
defaultProvider.resolveStringEvaluation('test-flag', '', evaluationContext, loggerMock),
432-
).rejects.toThrow(FlagsmithProviderError);
377+
).rejects.toThrow(GeneralError);
433378
});
434379

435380
it('should return default value with error details when flag value is undefined', async () => {
@@ -556,7 +501,7 @@ describe('FlagsmithOpenFeatureProvider', () => {
556501

557502
await expect(
558503
defaultProvider.resolveBooleanEvaluation('test-flag', false, evaluationContext, loggerMock),
559-
).rejects.toThrow(FlagsmithProviderError);
504+
).rejects.toThrow(GeneralError);
560505
});
561506

562507
it('should throw FlagNotFoundError when flag is not found and returnValueForDisabledFlags is false', async () => {
@@ -566,9 +511,11 @@ describe('FlagsmithOpenFeatureProvider', () => {
566511
useBooleanConfigValue: false,
567512
});
568513
mockFlagsmith.getIdentityFlags.mockRejectedValue(new Error('not found'));
569-
await expect(provider.resolveBooleanEvaluation('not-exist', false, {}, loggerMock)).rejects.toThrow(
570-
FlagNotFoundError,
571-
);
514+
const result = await provider.resolveBooleanEvaluation('not-exist', false, {}, loggerMock);
515+
expect(result.value).toBe(false);
516+
expect(result.reason).toBe(StandardResolutionReasons.ERROR);
517+
expect(result.errorCode).toBe(ErrorCode.FLAG_NOT_FOUND);
518+
expect(result.errorMessage).toContain("Flag 'not-exist' was not found.");
572519
});
573520

574521
it('should return default value with error details when flag value type does not match requested type', async () => {

libs/providers/flagsmith/src/lib/flagsmith-provider.ts

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@ import {
66
JsonValue,
77
ErrorCode,
88
FlagValueType,
9-
FlagNotFoundError,
109
Logger,
11-
ProviderStatus,
12-
OpenFeatureEventEmitter,
13-
ProviderEvents,
1410
StandardResolutionReasons,
1511
FlagValue,
12+
GeneralError,
1613
} from '@openfeature/server-sdk';
1714
import { Flags, Flagsmith, BaseFlag, TraitConfig, FlagsmithValue } from 'flagsmith-nodejs';
18-
import { FlagsmithProviderError } from './exceptions';
1915
import { typeFactory } from './type-factory';
2016

2117
type FlagsmithTrait = Record<string, FlagsmithValue | TraitConfig>;
@@ -39,29 +35,11 @@ export default class FlagsmithOpenFeatureProvider implements Provider {
3935

4036
readonly runsOn = 'server';
4137

42-
public readonly events = new OpenFeatureEventEmitter();
43-
private _status: ProviderStatus = ProviderStatus.NOT_READY;
44-
45-
get status(): ProviderStatus {
46-
return this._status;
47-
}
48-
4938
private client: Flagsmith;
5039
private returnValueForDisabledFlags: boolean;
5140
private useFlagsmithDefaults: boolean;
5241
private useBooleanConfigValue: boolean;
5342

54-
private set status(status: ProviderStatus) {
55-
if (this._status !== status) {
56-
this._status = status;
57-
if (status === ProviderStatus.READY) {
58-
this.events.emit(ProviderEvents.Ready);
59-
} else if (status === ProviderStatus.ERROR) {
60-
this.events.emit(ProviderEvents.Error);
61-
}
62-
}
63-
}
64-
6543
/**
6644
* Creates a Flagsmith OpenFeature provider.
6745
*
@@ -111,13 +89,18 @@ export default class FlagsmithOpenFeatureProvider implements Provider {
11189
const flags = await this.getFlags(evaluationContext);
11290
flag = flags.getFlag(flagKey);
11391
} catch (error) {
114-
throw new FlagsmithProviderError('An error occurred retrieving flags from Flagsmith client.', ErrorCode.GENERAL, {
92+
throw new GeneralError('An error occurred retrieving flags from Flagsmith client.', {
11593
cause: error as Error,
11694
});
11795
}
11896

11997
if (!flag || (!this.useFlagsmithDefaults && flag.isDefault)) {
120-
throw new FlagNotFoundError(`Flag '${flagKey}' was not found.`);
98+
return {
99+
value: defaultValue,
100+
reason: StandardResolutionReasons.ERROR,
101+
errorCode: ErrorCode.FLAG_NOT_FOUND,
102+
errorMessage: `Flag '${flagKey}' was not found.`,
103+
};
121104
}
122105

123106
if (!this.useBooleanConfigValue && flagType === 'boolean') {
@@ -128,7 +111,7 @@ export default class FlagsmithOpenFeatureProvider implements Provider {
128111
}
129112

130113
if (!(this.returnValueForDisabledFlags || flag.enabled)) {
131-
throw new FlagsmithProviderError(`Flag '${flagKey}' is not enabled.`, ErrorCode.GENERAL);
114+
throw new GeneralError(`Flag '${flagKey}' is not enabled.`);
132115
}
133116

134117
const typedValue = typeFactory(flag.value, flagType);
@@ -138,7 +121,6 @@ export default class FlagsmithOpenFeatureProvider implements Provider {
138121
reason: StandardResolutionReasons.ERROR,
139122
errorCode: ErrorCode.TYPE_MISMATCH,
140123
errorMessage: `Flag value ${flag.value} is not of type ${flagType}`,
141-
flagMetadata: {},
142124
};
143125
}
144126

@@ -194,14 +176,8 @@ export default class FlagsmithOpenFeatureProvider implements Provider {
194176
async initialize(context?: EvaluationContext): Promise<void> {
195177
try {
196178
await this.getFlags(context || {});
197-
this.status = ProviderStatus.READY;
198179
} catch (error) {
199-
this.status = ProviderStatus.ERROR;
200180
throw error;
201181
}
202182
}
203-
204-
async onClose(): Promise<void> {
205-
this.status = ProviderStatus.NOT_READY;
206-
}
207183
}

0 commit comments

Comments
 (0)