Skip to content

Commit e8c97e7

Browse files
adds graceful failure mode; all public functions have a try/catch (FF… (#16)
* adds graceful failure mode; all public functions have a try/catch (FF-968) * refactor error into single function; add test coverage * td reset lol
1 parent 45ec58b commit e8c97e7

File tree

2 files changed

+147
-49
lines changed

2 files changed

+147
-49
lines changed

src/client/eppo-client.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,67 @@ describe('EppoClient E2E test', () => {
130130
},
131131
};
132132

133+
describe('error encountered', () => {
134+
let client: EppoClient;
135+
const mockHooks = td.object<IAssignmentHooks>();
136+
137+
beforeAll(() => {
138+
storage.setEntries({ [flagKey]: mockExperimentConfig });
139+
client = new EppoClient(storage);
140+
141+
td.replace(EppoClient.prototype, 'getAssignmentVariation', function () {
142+
throw new Error('So Graceful Error');
143+
});
144+
});
145+
146+
afterAll(() => {
147+
td.reset();
148+
});
149+
150+
it('returns null when graceful failure if error encountered', async () => {
151+
client.setIsGracefulFailureMode(true);
152+
153+
expect(client.getAssignment('subject-identifer', flagKey, {}, mockHooks)).toBeNull();
154+
expect(client.getBoolAssignment('subject-identifer', flagKey, {}, mockHooks)).toBeNull();
155+
expect(
156+
client.getJSONStringAssignment('subject-identifer', flagKey, {}, mockHooks),
157+
).toBeNull();
158+
expect(client.getNumericAssignment('subject-identifer', flagKey, {}, mockHooks)).toBeNull();
159+
expect(
160+
client.getParsedJSONAssignment('subject-identifer', flagKey, {}, mockHooks),
161+
).toBeNull();
162+
expect(client.getStringAssignment('subject-identifer', flagKey, {}, mockHooks)).toBeNull();
163+
});
164+
165+
it('throws error when graceful failure is false', async () => {
166+
client.setIsGracefulFailureMode(false);
167+
168+
expect(() => {
169+
client.getAssignment('subject-identifer', flagKey, {}, mockHooks);
170+
}).toThrow();
171+
172+
expect(() => {
173+
client.getBoolAssignment('subject-identifer', flagKey, {}, mockHooks);
174+
}).toThrow();
175+
176+
expect(() => {
177+
client.getJSONStringAssignment('subject-identifer', flagKey, {}, mockHooks);
178+
}).toThrow();
179+
180+
expect(() => {
181+
client.getParsedJSONAssignment('subject-identifer', flagKey, {}, mockHooks);
182+
}).toThrow();
183+
184+
expect(() => {
185+
client.getNumericAssignment('subject-identifer', flagKey, {}, mockHooks);
186+
}).toThrow();
187+
188+
expect(() => {
189+
client.getStringAssignment('subject-identifer', flagKey, {}, mockHooks);
190+
}).toThrow();
191+
});
192+
});
193+
133194
describe('setLogger', () => {
134195
beforeAll(() => {
135196
storage.setEntries({ [flagKey]: mockExperimentConfig });

src/client/eppo-client.ts

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export interface IEppoClient {
8989
export default class EppoClient implements IEppoClient {
9090
private queuedEvents: IAssignmentEvent[] = [];
9191
private assignmentLogger: IAssignmentLogger | undefined;
92+
private isGracefulFailureMode = true;
9293

9394
constructor(private configurationStore: IConfigurationStore) {}
9495

@@ -100,10 +101,14 @@ export default class EppoClient implements IEppoClient {
100101
subjectAttributes: Record<string, any> = {},
101102
assignmentHooks?: IAssignmentHooks | undefined,
102103
): string | null {
103-
return (
104-
this.getAssignmentVariation(subjectKey, flagKey, subjectAttributes, assignmentHooks)
105-
.stringValue ?? null
106-
);
104+
try {
105+
return (
106+
this.getAssignmentVariation(subjectKey, flagKey, subjectAttributes, assignmentHooks)
107+
.stringValue ?? null
108+
);
109+
} catch (error) {
110+
return this.rethrowIfNotGraceful(error);
111+
}
107112
}
108113

109114
public getStringAssignment(
@@ -113,15 +118,19 @@ export default class EppoClient implements IEppoClient {
113118
subjectAttributes: Record<string, any> = {},
114119
assignmentHooks?: IAssignmentHooks | undefined,
115120
): string | null {
116-
return (
117-
this.getAssignmentVariation(
118-
subjectKey,
119-
flagKey,
120-
subjectAttributes,
121-
assignmentHooks,
122-
ValueType.StringType,
123-
).stringValue ?? null
124-
);
121+
try {
122+
return (
123+
this.getAssignmentVariation(
124+
subjectKey,
125+
flagKey,
126+
subjectAttributes,
127+
assignmentHooks,
128+
ValueType.StringType,
129+
).stringValue ?? null
130+
);
131+
} catch (error) {
132+
return this.rethrowIfNotGraceful(error);
133+
}
125134
}
126135

127136
getBoolAssignment(
@@ -131,15 +140,19 @@ export default class EppoClient implements IEppoClient {
131140
subjectAttributes: Record<string, any> = {},
132141
assignmentHooks?: IAssignmentHooks | undefined,
133142
): boolean | null {
134-
return (
135-
this.getAssignmentVariation(
136-
subjectKey,
137-
flagKey,
138-
subjectAttributes,
139-
assignmentHooks,
140-
ValueType.BoolType,
141-
).boolValue ?? null
142-
);
143+
try {
144+
return (
145+
this.getAssignmentVariation(
146+
subjectKey,
147+
flagKey,
148+
subjectAttributes,
149+
assignmentHooks,
150+
ValueType.BoolType,
151+
).boolValue ?? null
152+
);
153+
} catch (error) {
154+
return this.rethrowIfNotGraceful(error);
155+
}
143156
}
144157

145158
getNumericAssignment(
@@ -148,15 +161,19 @@ export default class EppoClient implements IEppoClient {
148161
subjectAttributes?: Record<string, EppoValue>,
149162
assignmentHooks?: IAssignmentHooks | undefined,
150163
): number | null {
151-
return (
152-
this.getAssignmentVariation(
153-
subjectKey,
154-
flagKey,
155-
subjectAttributes,
156-
assignmentHooks,
157-
ValueType.NumericType,
158-
).numericValue ?? null
159-
);
164+
try {
165+
return (
166+
this.getAssignmentVariation(
167+
subjectKey,
168+
flagKey,
169+
subjectAttributes,
170+
assignmentHooks,
171+
ValueType.NumericType,
172+
).numericValue ?? null
173+
);
174+
} catch (error) {
175+
return this.rethrowIfNotGraceful(error);
176+
}
160177
}
161178

162179
public getJSONStringAssignment(
@@ -166,15 +183,19 @@ export default class EppoClient implements IEppoClient {
166183
subjectAttributes: Record<string, any> = {},
167184
assignmentHooks?: IAssignmentHooks | undefined,
168185
): string | null {
169-
return (
170-
this.getAssignmentVariation(
171-
subjectKey,
172-
flagKey,
173-
subjectAttributes,
174-
assignmentHooks,
175-
ValueType.JSONType,
176-
).stringValue ?? null
177-
);
186+
try {
187+
return (
188+
this.getAssignmentVariation(
189+
subjectKey,
190+
flagKey,
191+
subjectAttributes,
192+
assignmentHooks,
193+
ValueType.JSONType,
194+
).stringValue ?? null
195+
);
196+
} catch (error) {
197+
return this.rethrowIfNotGraceful(error);
198+
}
178199
}
179200

180201
public getParsedJSONAssignment(
@@ -184,15 +205,27 @@ export default class EppoClient implements IEppoClient {
184205
subjectAttributes: Record<string, any> = {},
185206
assignmentHooks?: IAssignmentHooks | undefined,
186207
): object | null {
187-
return (
188-
this.getAssignmentVariation(
189-
subjectKey,
190-
flagKey,
191-
subjectAttributes,
192-
assignmentHooks,
193-
ValueType.JSONType,
194-
).objectValue ?? null
195-
);
208+
try {
209+
return (
210+
this.getAssignmentVariation(
211+
subjectKey,
212+
flagKey,
213+
subjectAttributes,
214+
assignmentHooks,
215+
ValueType.JSONType,
216+
).objectValue ?? null
217+
);
218+
} catch (error) {
219+
return this.rethrowIfNotGraceful(error);
220+
}
221+
}
222+
223+
private rethrowIfNotGraceful(err: Error): null {
224+
if (this.isGracefulFailureMode) {
225+
console.error(`[Eppo SDK] Error getting assignment: ${err.message}`);
226+
return null;
227+
}
228+
throw err;
196229
}
197230

198231
private getAssignmentVariation(
@@ -288,6 +321,10 @@ export default class EppoClient implements IEppoClient {
288321
this.flushQueuedEvents(); // log any events that may have been queued while initializing
289322
}
290323

324+
public setIsGracefulFailureMode(gracefulFailureMode: boolean) {
325+
this.isGracefulFailureMode = gracefulFailureMode;
326+
}
327+
291328
private flushQueuedEvents() {
292329
const eventsToFlush = this.queuedEvents;
293330
this.queuedEvents = [];

0 commit comments

Comments
 (0)