diff --git a/packages/angular/angular.json b/packages/angular/angular.json index 3fdc6e91d8..1d9cc72d76 100644 --- a/packages/angular/angular.json +++ b/packages/angular/angular.json @@ -39,6 +39,7 @@ "cli": { "schematicCollections": [ "@angular-eslint/schematics" - ] + ], + "analytics": false } } diff --git a/packages/server/src/client/client.ts b/packages/server/src/client/client.ts index 6c31fa6eba..4856469439 100644 --- a/packages/server/src/client/client.ts +++ b/packages/server/src/client/client.ts @@ -8,12 +8,14 @@ import { import { Features } from '../evaluation'; import { ProviderStatus } from '../provider'; import { ProviderEvents } from '../events'; +import { Tracking } from '../tracking'; export interface Client extends EvaluationLifeCycle, Features, ManageContext, ManageLogger, + Tracking, Eventing { readonly metadata: ClientMetadata; /** diff --git a/packages/server/src/client/internal/open-feature-client.ts b/packages/server/src/client/internal/open-feature-client.ts index 0e3cd1bbc1..efb69e4594 100644 --- a/packages/server/src/client/internal/open-feature-client.ts +++ b/packages/server/src/client/internal/open-feature-client.ts @@ -9,6 +9,7 @@ import { HookContext, JsonValue, Logger, + OccurrenceDetails, OpenFeatureError, ProviderFatalError, ProviderNotReadyError, @@ -221,6 +222,10 @@ export class OpenFeatureClient implements Client { return this.evaluate(flagKey, this._provider.resolveObjectEvaluation, defaultValue, 'object', context, options); } + track(occurrenceKey: string, context: EvaluationContext, occurrenceDetails: OccurrenceDetails): void { + return this._provider.track?.(occurrenceKey, context, occurrenceDetails); + } + private async evaluate( flagKey: string, resolver: ( diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c229d16fb4..249135cbf8 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -5,4 +5,5 @@ export * from './open-feature'; export * from './transaction-context'; export * from './events'; export * from './hooks'; +export * from './tracking'; export * from '@openfeature/core'; diff --git a/packages/server/src/provider/provider.ts b/packages/server/src/provider/provider.ts index 63173d3d0e..a8e90a2468 100644 --- a/packages/server/src/provider/provider.ts +++ b/packages/server/src/provider/provider.ts @@ -1,4 +1,12 @@ -import { CommonProvider, EvaluationContext, JsonValue, Logger, ResolutionDetails, ServerProviderStatus } from '@openfeature/core'; +import { + CommonProvider, + EvaluationContext, + JsonValue, + Logger, + OccurrenceDetails, + ResolutionDetails, + ServerProviderStatus, +} from '@openfeature/core'; import { Hook } from '../hooks'; export { ServerProviderStatus as ProviderStatus }; @@ -57,4 +65,12 @@ export interface Provider extends CommonProvider { context: EvaluationContext, logger: Logger, ): Promise>; + + /** + * Track a thing + * @param occurrenceKey + * @param context + * @param occurrenceDetails + */ + track?(occurrenceKey: string, context: EvaluationContext, occurrenceDetails: OccurrenceDetails): void; } diff --git a/packages/server/src/tracking/index.ts b/packages/server/src/tracking/index.ts new file mode 100644 index 0000000000..5794a1d40f --- /dev/null +++ b/packages/server/src/tracking/index.ts @@ -0,0 +1 @@ +export * from './tracking'; diff --git a/packages/server/src/tracking/tracking.ts b/packages/server/src/tracking/tracking.ts new file mode 100644 index 0000000000..9ae6f9597c --- /dev/null +++ b/packages/server/src/tracking/tracking.ts @@ -0,0 +1,12 @@ +import { EvaluationContext, OccurrenceDetails } from '@openfeature/core'; + +export interface Tracking { + + /** + * Track a thing + * @param occurrenceKey + * @param context + * @param occurrenceDetails + */ + track(occurrenceKey: string, context: EvaluationContext, occurrenceDetails: OccurrenceDetails): void; +} diff --git a/packages/shared/src/evaluation/context.ts b/packages/shared/src/evaluation/context.ts index 14de9dbfd7..4cde4b0789 100644 --- a/packages/shared/src/evaluation/context.ts +++ b/packages/shared/src/evaluation/context.ts @@ -1,4 +1,4 @@ -import { PrimitiveValue } from './evaluation'; +import { PrimitiveValue } from '../types'; export type EvaluationContextValue = | PrimitiveValue diff --git a/packages/shared/src/evaluation/evaluation.ts b/packages/shared/src/evaluation/evaluation.ts index c900b40f2a..dfb0b02a76 100644 --- a/packages/shared/src/evaluation/evaluation.ts +++ b/packages/shared/src/evaluation/evaluation.ts @@ -1,13 +1,6 @@ -export type FlagValueType = 'boolean' | 'string' | 'number' | 'object'; - -export type PrimitiveValue = null | boolean | string | number; -export type JsonObject = { [key: string]: JsonValue }; -export type JsonArray = JsonValue[]; +import { JsonValue } from '../types/structure'; -/** - * Represents a JSON node value. - */ -export type JsonValue = PrimitiveValue | JsonObject | JsonArray; +export type FlagValueType = 'boolean' | 'string' | 'number' | 'object'; /** * Represents a JSON node value, or Date. diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 8a85f41420..42c1504a05 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -7,4 +7,5 @@ export * from './logger'; export * from './provider'; export * from './evaluation'; export * from './type-guards'; +export * from './tracking'; export * from './open-feature'; diff --git a/packages/shared/src/tracking/index.ts b/packages/shared/src/tracking/index.ts new file mode 100644 index 0000000000..47af9db549 --- /dev/null +++ b/packages/shared/src/tracking/index.ts @@ -0,0 +1 @@ +export * from './occurrence'; diff --git a/packages/shared/src/tracking/occurrence.ts b/packages/shared/src/tracking/occurrence.ts new file mode 100644 index 0000000000..db7a73ca5a --- /dev/null +++ b/packages/shared/src/tracking/occurrence.ts @@ -0,0 +1,20 @@ +import { PrimitiveValue } from '../types'; + + +export type OccurrenceValue = + | PrimitiveValue + | Date + | { [key: string]: OccurrenceValue } + | OccurrenceValue[]; + +/** + * A container for arbitrary contextual data that can be used as a basis for dynamic evaluation + */ +export type OccurrenceDetails = { + /** + * A string uniquely identifying the subject (end-user, or client service) of a flag evaluation. + * Providers may require this field for fractional flag evaluation, rules, or overrides targeting specific users. + * Such providers may behave unpredictably if a targeting key is not specified at flag resolution. + */ + value?: number; +} & Record; diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index 69cf2f63f4..6c96c078ae 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -1,2 +1,3 @@ export * from './metadata'; -export * from './paradigm'; \ No newline at end of file +export * from './paradigm'; +export * from './structure'; diff --git a/packages/shared/src/types/structure.ts b/packages/shared/src/types/structure.ts new file mode 100644 index 0000000000..7edbdd9370 --- /dev/null +++ b/packages/shared/src/types/structure.ts @@ -0,0 +1,7 @@ +export type PrimitiveValue = null | boolean | string | number; +export type JsonObject = { [key: string]: JsonValue }; +export type JsonArray = JsonValue[]; +/** + * Represents a JSON node value. + */ +export type JsonValue = PrimitiveValue | JsonObject | JsonArray; diff --git a/packages/web/src/client/client.ts b/packages/web/src/client/client.ts index 699359e68c..9f8b8502f6 100644 --- a/packages/web/src/client/client.ts +++ b/packages/web/src/client/client.ts @@ -2,8 +2,14 @@ import { ClientMetadata, EvaluationLifeCycle, Eventing, ManageLogger } from '@op import { Features } from '../evaluation'; import { ProviderStatus } from '../provider'; import { ProviderEvents } from '../events'; +import { Tracking } from '../tracking'; -export interface Client extends EvaluationLifeCycle, Features, ManageLogger, Eventing { +export interface Client + extends EvaluationLifeCycle, + Features, + ManageLogger, + Eventing, + Tracking { readonly metadata: ClientMetadata; /** * Returns the status of the associated provider. diff --git a/packages/web/src/client/internal/open-feature-client.ts b/packages/web/src/client/internal/open-feature-client.ts index 7e649e39dd..b2c8638422 100644 --- a/packages/web/src/client/internal/open-feature-client.ts +++ b/packages/web/src/client/internal/open-feature-client.ts @@ -9,6 +9,7 @@ import { HookContext, JsonValue, Logger, + OccurrenceDetails, OpenFeatureError, ProviderFatalError, ProviderNotReadyError, @@ -179,6 +180,14 @@ export class OpenFeatureClient implements Client { return this.evaluate(flagKey, this._provider.resolveObjectEvaluation, defaultValue, 'object', options); } + track(occurrenceKey: string, occurrenceDetails: OccurrenceDetails): void { + const context = { + ...OpenFeature.getContext(this?.options?.domain), + }; + + return this._provider.track?.(occurrenceKey, context, occurrenceDetails); + } + private evaluate( flagKey: string, resolver: (flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger) => ResolutionDetails, diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index 355cd9ce1c..fa481b7336 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -4,4 +4,5 @@ export * from './evaluation'; export * from './open-feature'; export * from './events'; export * from './hooks'; +export * from './tracking'; export * from '@openfeature/core'; diff --git a/packages/web/src/provider/provider.ts b/packages/web/src/provider/provider.ts index 6b51ec73fa..c3b24182f3 100644 --- a/packages/web/src/provider/provider.ts +++ b/packages/web/src/provider/provider.ts @@ -1,4 +1,4 @@ -import { ClientProviderStatus, CommonProvider, EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfeature/core'; +import { ClientProviderStatus, CommonProvider, EvaluationContext, JsonValue, Logger, OccurrenceDetails, ResolutionDetails } from '@openfeature/core'; import { Hook } from '../hooks'; export { ClientProviderStatus as ProviderStatus }; @@ -71,4 +71,12 @@ export interface Provider extends CommonProvider { context: EvaluationContext, logger: Logger, ): ResolutionDetails; + + /** + * Track a thing + * @param occurrenceKey + * @param context + * @param occurrenceDetails + */ + track?(occurrenceKey: string, context: EvaluationContext, occurrenceDetails: OccurrenceDetails): void; } diff --git a/packages/web/src/tracking/index.ts b/packages/web/src/tracking/index.ts new file mode 100644 index 0000000000..5794a1d40f --- /dev/null +++ b/packages/web/src/tracking/index.ts @@ -0,0 +1 @@ +export * from './tracking'; diff --git a/packages/web/src/tracking/tracking.ts b/packages/web/src/tracking/tracking.ts new file mode 100644 index 0000000000..f6137f474a --- /dev/null +++ b/packages/web/src/tracking/tracking.ts @@ -0,0 +1,11 @@ +import { OccurrenceDetails } from '@openfeature/core'; + +export interface Tracking { + + /** + * Track a thing + * @param occurrenceKey + * @param occurrenceDetails + */ + track(occurrenceKey: string, occurrenceDetails: OccurrenceDetails): void; +}