Repository: open-feature/js-sdk
Pull request #1100 (2024-12-09 22:36:01)

refactor: improve track interface for providers
## This PR

- updates the context and trackingEventDetails on the tracking interface to not be optional since they're always supplied by the SDK.

### Notes

This is a small QoL improvement for provider devs implementing the tracking interface.

### How to test

The SDK still compiles, and the tests don't need to be modified.



toddbaert: I pushed up some changes to actually default the tracking contents to match your types. I thought we were doing this, but we weren't. I also added a test.

Patch:
diff --git a/packages/server/src/client/internal/open-feature-client.ts b/packages/server/src/client/internal/open-feature-client.ts
index 663e1bb3..bd9a4e5a 100644
--- a/packages/server/src/client/internal/open-feature-client.ts
+++ b/packages/server/src/client/internal/open-feature-client.ts
@@ -226,7 +226,7 @@ export class OpenFeatureClient implements Client {
     return this.evaluate<T>(flagKey, this._provider.resolveObjectEvaluation, defaultValue, 'object', context, options);
   }
 
-  track(occurrenceKey: string, context: EvaluationContext, occurrenceDetails: TrackingEventDetails): void {
+  track(occurrenceKey: string, context: EvaluationContext = {}, occurrenceDetails: TrackingEventDetails = {}): void {
     try {
       this.shortCircuitIfNotReady();
 
diff --git a/packages/shared/src/provider/provider.ts b/packages/shared/src/provider/provider.ts
index e964f1b8..74ae8606 100644
--- a/packages/shared/src/provider/provider.ts
+++ b/packages/shared/src/provider/provider.ts
@@ -133,5 +133,5 @@ export interface CommonProvider<S extends ClientProviderStatus | ServerProviderS
    * @param context
    * @param trackingEventDetails
    */
-  track?(trackingEventName: string, context?: EvaluationContext, trackingEventDetails?: TrackingEventDetails): void;
+  track?(trackingEventName: string, context: EvaluationContext, trackingEventDetails: TrackingEventDetails): void;
 }
diff --git a/packages/web/src/client/internal/open-feature-client.ts b/packages/web/src/client/internal/open-feature-client.ts
index 6ce25886..43f646f8 100644
--- a/packages/web/src/client/internal/open-feature-client.ts
+++ b/packages/web/src/client/internal/open-feature-client.ts
@@ -183,7 +183,7 @@ export class OpenFeatureClient implements Client {
     return this.evaluate<T>(flagKey, this._provider.resolveObjectEvaluation, defaultValue, 'object', options);
   }
 
-  track(occurrenceKey: string, occurrenceDetails: TrackingEventDetails): void {
+  track(occurrenceKey: string, occurrenceDetails: TrackingEventDetails = {}): void {
     try {
       this.shortCircuitIfNotReady();
 


Test patch:
diff --git a/packages/server/test/client.spec.ts b/packages/server/test/client.spec.ts
index 69dc41fa..b1db2741 100644
--- a/packages/server/test/client.spec.ts
+++ b/packages/server/test/client.spec.ts
@@ -859,6 +859,18 @@ describe('OpenFeatureClient', () => {
         }).not.toThrow();
       });
 
+      it('provide empty tracking details to provider if not supplied in call', async () => {
+        await OpenFeature.setProviderAndWait({ ...MOCK_PROVIDER });
+        const client = OpenFeature.getClient();
+        client.track(eventName);
+
+        expect(MOCK_PROVIDER.track).toHaveBeenCalledWith(
+          eventName,
+          expect.any(Object),
+          expect.any(Object),
+        );
+      });
+
       it('should call provider with correct context', async () => {
         await OpenFeature.setProviderAndWait({ ...MOCK_PROVIDER });
         OpenFeature.setContext({ [globalContextKey]: globalContextValue });
diff --git a/packages/web/test/client.spec.ts b/packages/web/test/client.spec.ts
index 381dd9b2..c67b593a 100644
--- a/packages/web/test/client.spec.ts
+++ b/packages/web/test/client.spec.ts
@@ -655,6 +655,18 @@ describe('OpenFeatureClient', () => {
         }).not.toThrow();
       });
 
+      it('provide empty tracking details to provider if not supplied in call', async () => {
+        await OpenFeature.setProviderAndWait({ ...MOCK_PROVIDER });
+        const client = OpenFeature.getClient();
+        client.track(eventName);
+
+        expect(MOCK_PROVIDER.track).toHaveBeenCalledWith(
+          eventName,
+          expect.any(Object),
+          expect.any(Object),
+        );
+      });
+
       it('should no-op and not throw if provider throws', async () => {
         await OpenFeature.setProviderAndWait({
           ...MOCK_PROVIDER,