diff --git a/.travis.yml b/.travis.yml index 8871ab1d..bf9ce922 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,17 +22,17 @@ jobs: - find . -type f -name '*.md' -exec awesome_bot {} \; notifications: email: false - - stage: 'Lint markdown files' - os: linux - language: generic - before_install: skip - install: - - npm i -g markdown-spellcheck - before_script: - - wget --quiet https://raw.githubusercontent.com/optimizely/mdspell-config/master/.spelling - script: - - mdspell -a -n -r --en-us '**/*.md' - after_success: skip +# - stage: 'Lint markdown files' +# os: linux +# language: generic +# before_install: skip +# install: +# - npm i -g markdown-spellcheck +# before_script: +# - wget --quiet https://raw.githubusercontent.com/optimizely/mdspell-config/master/.spelling +# script: +# - mdspell -a -n -r --en-us '**/*.md' +# after_success: skip - &integrationtest stage: 'Integration tests' env: SDK=csharp SDK_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH diff --git a/docs/docs/sdk-reference-guides/010 - install-sdk-csharp.md b/docs/docs/sdk-reference-guides/010 - install-sdk-csharp.md new file mode 100644 index 00000000..8b17af31 --- /dev/null +++ b/docs/docs/sdk-reference-guides/010 - install-sdk-csharp.md @@ -0,0 +1,24 @@ +--- +title: "Install SDK" +slug: "install-sdk-csharp" +hidden: false +createdAt: "2019-09-11T13:55:51.015Z" +updatedAt: "2019-09-11T13:58:07.201Z" +--- +TEST CHANGE + +The C# SDK is distributed through NuGet. + +For Windows, to install, run the command `Install-Package Optimizely.SDK` in the Package Manager Console: +[block:code] +{ + "codes": [ + { + "code": "PM> Install-Package Optimizely.SDK -Version 3.0.0\n\n", + "language": "shell", + "name": "Install the SDK" + } + ] +} +[/block] +The package is on NuGet at https://www.nuget.org/packages/Optimizely.SDK. The full source code is at https://github.com/optimizely/csharp-sdk. \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/010 -install-sdk-csharp.md b/docs/docs/sdk-reference-guides/010 -install-sdk-csharp.md new file mode 100644 index 00000000..e02ebfae --- /dev/null +++ b/docs/docs/sdk-reference-guides/010 -install-sdk-csharp.md @@ -0,0 +1,22 @@ +--- +title: "Install SDK" +slug: "install-sdk-csharp" +hidden: false +createdAt: "2019-09-11T13:55:51.015Z" +updatedAt: "2019-09-11T13:58:07.201Z" +--- +The C# SDK is distributed through NuGet. + +For Windows, to install, run the command `Install-Package Optimizely.SDK` in the Package Manager Console: +[block:code] +{ + "codes": [ + { + "code": "PM> Install-Package Optimizely.SDK -Version 3.0.0\n\n", + "language": "shell", + "name": "Install the SDK" + } + ] +} +[/block] +The package is on NuGet at https://www.nuget.org/packages/Optimizely.SDK. The full source code is at https://github.com/optimizely/csharp-sdk. \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/020 - initialize-sdk-csharp.md b/docs/docs/sdk-reference-guides/020 - initialize-sdk-csharp.md new file mode 100644 index 00000000..afd5d63f --- /dev/null +++ b/docs/docs/sdk-reference-guides/020 - initialize-sdk-csharp.md @@ -0,0 +1,253 @@ +--- +title: "Initialize SDK" +slug: "initialize-sdk-csharp" +hidden: false +createdAt: "2019-09-11T14:15:47.848Z" +updatedAt: "2020-04-10T00:11:24.283Z" +--- +Use the `instantiate` method to initialize the C# SDK and instantiate an instance of the Optimizely client class that exposes API methods like [Get Enabled Features](doc:get-enabled-features-csharp). Each client corresponds to the datafile representing the state of a project for a certain environment. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.2.0 +[block:api-header] +{ + "title": "Description" +} +[/block] +The constructor accepts a configuration object to configure Optimizely. + +Some parameters are optional because the SDK provides a default implementation, but you may want to override these for your production environments. For example, you may want override these to set up an [error handler](doc:customize-error-handler-csharp) and [logger](doc:customize-logger-csharp) to catch issues, an event dispatcher to manage network calls, and a User Profile Service to ensure sticky bucketing. +[block:api-header] +{ + "title": "Parameters" +} +[/block] +The table below lists the required and optional parameters in C#. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**datafile**\n*optional* ", + "0-1": "string", + "0-2": "The JSON string representing the project.", + "1-0": "**configManager**\n*optional*", + "1-1": "ProjectConfigManager", + "1-2": "The project config manager provides the project config to the client.", + "2-0": "**eventDispatcher**\n*optional*", + "2-1": "IEventDispatcher", + "2-2": "An event handler to manage network calls.", + "3-0": "**logger**\n*optional*", + "3-1": "ILogger", + "3-2": "A logger implementation to log issues.", + "4-0": "**errorHandler**\n*optional*", + "4-1": "IErrorHandler", + "4-2": "An error handler object to handle errors.", + "5-0": "**userProfileService**\n*optional*", + "5-1": "UserProfileService", + "5-2": "A user profile service.", + "6-0": "**skipJsonValidation**\n*optional*", + "6-1": "boolean", + "6-2": "Specifies whether the JSON should be validated. Set to `true` to skip JSON validation on the schema, or `false` to perform validation." + }, + "cols": 3, + "rows": 7 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +Instantiates an instance of the Optimzely class. +[block:api-header] +{ + "title": "Automatic datafile management (ADM)" +} +[/block] +Optimizely provides out-of-the-box functionality to dynamically manage datafiles (configuration files) on either the client or the server. The C# SDK provides default implementations of an Optimizely `ProjectConfigManager`. The package also includes a factory class, OptimizelyFactory, which you can use to instantiate the Optimizely SDK with the default configuration of HttpProjectConfigManager. + +Whenever the experiment configuration changes, the SDK uses automatic datafile management (ADM) to handle the change for you. In the C# SDK, you can provide either `sdkKey` or `datafile` or both. + +* When initializing with just the SDK key, the SDK will poll for datafile changes in the background at regular intervals. +* When initializing with just the datafile, the SDK will NOT poll for datafile changes in the background. +* When initializing with both the SDK key and datafile, the SDK will use the given datafile and start polling for datafile changes in the background. + +### Basic example + +The following code example shows basic C# ADM usage. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\n\npublic class App\n{\n public static void Main(string[] args)\n {\n string sdkKey = args[0];\n \t Optimizely optimizely = OptimizelyFactory.NewDefaultInstance(sdkKey);\n }\n}\n\n", + "language": "csharp" + } + ] +} +[/block] +### Advanced examples + + +[block:callout] +{ + "type": "warning", + "body": "If you are configuring a logger, make sure to pass it into the `ProjectConfigManager` instance as well." +} +[/block] +In the C# SDK, you only need to pass the SDK key value to instantiate a client. Whenever the experiment configuration changes, the SDK handles the change for you. + +Include `sdkKey` as a string property in the options object you pass to the `createInstance` method. + +When you provide the `sdkKey`, the SDK instance downloads the datafile associated with that `sdkKey`. When the download completes, the SDK instance updates itself to use the downloaded datafile. +[block:callout] +{ + "type": "warning", + "title": "", + "body": "Pass all components (Logger, ErrorHandler, NotificationCenter) to the Optimizely constructor. Not passing a component will fail to enable its respective functionality. In other words, components only work when passed to the constructor." +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "// Initialize with SDK key and default configuration\nvar sdkKey = \"\" // replace with your own SDK Key\nOptimizely optimizely = OptimizelyFactory.newDefaultInstance(sdkKey);\n\n\n// You can also customize the SDK instance with custom configuration. In this example we are customizing the project config manager to poll every 5 minutes for the datafile.\nvar projectConfigManager =\n new HttpProjectConfigManager.Builder()\n .WithSdkKey(sdkKey)\n .WithPollingInterval(TimeSpan.FromMinutes(5))\n // .WithLogger(logger) - this is needed if you are configuring a logger for the optimizely instance\n// .WithErrorHandler(errorHandler) - this is needed if you are configuring an errorhandler for the optimizely instance.\n// .WithNotificationCenter(notificationCenter) this is needed if you are subscribing config update\n .Build();\n\nvar Optimizely = new Optimizely(projectConfigManager);\n\n// Initialize with Logger\n// var Optimizely = new Optimizely(projectConfigManager, logger: logger);\n\n// Initialize with Logger, ErrorHandler\n// var Optimizely = new Optimizely(projectConfigManager, errorHandler: errorHandler, logger: logger);\n\n// Initialize with NotificationCenter, Logger, ErrorHandler\n// var Optimizely = new Optimizely(projectConfigManager, notificationCenter: NotificationCenter, errorHandler: errorHandler, logger: logger);\n\n// Note: Use OptimizelyFactory NewDefaultInstance method to use same logger, errorHandler and notificationCenter for all of its Components (Optimizely, EventProcessor, HttpProjectConfigManager)\n", + "language": "csharp" + } + ] +} +[/block] +Here is a code example showing advanced configuration for C# ADM. Advanced configuration properties are described in the sections below. This advanced example shows how to construct the individual components directly to override various configurations. This gives you full control over which implementations to use and how to use them. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\nusing OptimizelySDK.Config;\n\npublic class App\n{\n public static void Main(string[] args)\n {\n string sdkKey = args[0];\n // You can also use your own implementation of the ProjectConfigManager interface\n ProjectConfigManager projectConfigManager =\n new HttpProjectConfigManager.Builder()\n\t .WithSdkKey(sdkKey)\n\t .WithPollingInterval(TimeSpan.FromMinutes(1))\n\t .Build();\n\n Optimizely optimizely = new Optimizely(configManager);\n }\n}\n\n", + "language": "csharp" + } + ] +} +[/block] +### HttpProjectConfigManager + +[HttpProjectConfigManager](https://github.com/optimizely/csharp-sdk/blob/fahad/dfm-readme/OptimizelySDK/Config/HttpProjectConfigManager.cs) is an implementation of the abstract [PollingProjectConfigManager](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Config/PollingProjectConfigManager.cs). The `Poll` method is extended and makes an HTTP `GET` request to the configured URL to asynchronously download the project datafile and initialize an instance of the `ProjectConfig`. + +By default, `HttpProjectConfigManager` will block until the first successful datafile retrieval, up to a configurable timeout. Set the frequency of the polling method and the blocking timeout with `HttpProjectConfigManager.Builder`. +[block:code] +{ + "codes": [ + { + "code": "ProjectConfigManager projectConfigManager =\n new HttpProjectConfigManager.Builder()\n\t .WithSdkKey(sdkKey)\n\t .WithPollingInterval(TimeSpan.FromMinutes(1))\n\t .Build();\n\n", + "language": "csharp" + } + ] +} +[/block] +#### SDK key + +The SDK key is used to compose the outbound HTTP request to the default datafile location on the Optimizely CDN. + +#### Polling interval + +The polling interval is used to specify a fixed delay between consecutive HTTP requests for the datafile. The valid interval duration is between 1 to 4294967294 milliseconds. + +#### Initial datafile + +You can provide an initial datafile via the builder to bootstrap the `ProjectConfigManager` so that it can be used immediately without blocking execution. The initial datafile also serves as a fallback datafile if HTTP connection cannot be established. This is useful in mobile environments, where internet connectivity is not guaranteed. + +The initial datafile will be discarded after the first successful datafile poll. + +#### Builder methods + +Use the following builder methods to customize the `HttpProjectConfigManager` configuration. +[block:parameters] +{ + "data": { + "0-0": "**WithDatafile(string)**", + "1-0": "**WithUrl(string)**", + "2-0": "**WithFormat(string)**", + "3-0": "**WithPollingInterval(TimeSpan)**", + "0-1": "null", + "1-1": "null", + "2-1": "null", + "3-2": "Fixed delay between fetches for the datafile", + "3-1": "5 minutes", + "1-2": "URL override location used to specify custom HTTP source for the Optimizely datafile", + "0-2": "Initial datafile, typically sourced from a local cached source", + "2-2": "Parameterized datafile URL by SDK key", + "4-0": "**WithBlockingTimeoutPeriod(TimeSpan)**", + "4-1": "15 seconds", + "h-0": "Property", + "h-1": "Default value", + "h-2": "Description", + "4-2": "Maximum time to wait for initial bootstrapping. The valid timeout duration is 1 to 4294967294 milliseconds.", + "5-0": "**WithSdkKey(string)**", + "5-1": "null", + "5-2": "Optimizely project SDK key; required unless source URL is overridden" + }, + "cols": 3, + "rows": 6 +} +[/block] +#### Update config notifications + +A notification signal will be triggered whenever a new datafile is fetched. To subscribe to these notifications, use method `NotificationCenter.AddNotification()`. +[block:code] +{ + "codes": [ + { + "code": "optimizely.NotificationCenter.AddNotification(\n NotificationCenter.NotificationType.OptimizelyConfigUpdate,\n () => Console.WriteLine(\"Received new datafile configuration\")\n);\n\n", + "language": "csharp" + } + ] +} +[/block] +### OptimizelyFactory + +[OptimizelyFactory](https://github.com/optimizely/csharp-sdk/blob/fahad/dfm-readme/OptimizelySDK/OptimizelyFactory.cs) provides basic utility to instantiate the Optimizely SDK with a minimal number of configuration options. + +OptimizelyFactory does not capture all configuration and initialization options. For more use cases, build the resources with their constructors. + +You must provide the SDK key at runtime, directly via the factory method: +[block:code] +{ + "codes": [ + { + "code": "Optimizely optimizely = OptimizelyFactory.NewDefaultInstance(<>);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Instantiate using datafile" +} +[/block] +You can also instantiate with a hard-coded datafile. If you don't pass in an SDK key, the Optimizely Client will not automatically sync newer versions of the datafile. Any time you retrieve an updated datafile, just re-instantiate the same client. + +For simple applications, all you need to provide to instantiate a client is a datafile specifying the project configuration for a given environment. For most advanced implementations, you'll want to [customize the logger](doc:customize-logger-csharp) or [error handler](doc:customize-error-handler-csharp) for your specific requirements. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\n\n// Instantiate an Optimizely client\nvar datafile = \"\"\nOptimizely OptimizelyClient = new Optimizely(datafile);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# are at [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/030 - example-usage-csharp.md b/docs/docs/sdk-reference-guides/030 - example-usage-csharp.md new file mode 100644 index 00000000..05c4f303 --- /dev/null +++ b/docs/docs/sdk-reference-guides/030 - example-usage-csharp.md @@ -0,0 +1,25 @@ +--- +title: "Example usage" +slug: "example-usage-csharp" +hidden: false +createdAt: "2019-09-11T22:26:38.446Z" +updatedAt: "2019-09-12T20:28:26.570Z" +--- +Once you've installed the C# SDK, import the Optimizely library into your code, get your Optimizely project's datafile, and instantiate a client. Then, you can use the client to evaluate feature flags, activate an A/B test, or feature test. + +This example demonstrates the basic usage of each of these concepts. This example shows how to: +1. Evaluate a feature with the key `price_filter` and check a configuration variable on it called `min_price`. The SDK evaluates your feature test and rollouts to determine whether the feature is enabled for a particular user, and which minimum price they should see if so. + +2. Run an A/B test called `app_redesign`. This experiment has two variations, `control` and `treatment`. It uses the `activate` method to assign the user to a variation, returning its key. As a side effect, the activate function also sends an impression event to Optimizely to record that the current user has been exposed to the experiment. + +3. Use event tracking to track an event called `purchased`. This conversion event measures the impact of an experiment. Using the track method, the purchase is automatically attributed back to the running A/B and feature tests we've activated, and the SDK sends a network request to Optimizely via the customizable event dispatcher so we can count it in your results page. +[block:code] +{ + "codes": [ + { + "code": "//Import Optimizely SDK\nusing OptimizelySDK;\n\n// Instantiate an Optimizely client\nvar optimizelyClient = new Optimizely(datafile);\n\n// Evaluate a feature flag and a variable\nbool isFeatureEnabled = optimizelyClient.IsFeatureEnabled(\"price_filter\", userId);\nint? min_price = optimizelyClient.GetFeatureVariableInteger(\"price_filter\", \"min_price\", userId);\n\n// Activate an A/B test\nvar variation = optimizelyClient.Activate(\"app_redesign\", userId);\n\tif (variation != null && !string.IsNullOrEmpty(variation.Key))\n\t{\n\t\tif (variation.Key == \"control\")\n\t\t{\n\t\t\t// Execute code for variation A\n\t\t}\n\t\telse if (variation.Key == \"treatment\")\n\t\t{\n\t\t\t// Execute code for variation B\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Execute code for your users who don’t qualify for the experiment\n\t}\n\n// Track an event\noptimizelyClient.Track(\"purchased\", userId);\n\n", + "language": "csharp" + } + ] +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/040 - optimizelyconfig-csharp.md b/docs/docs/sdk-reference-guides/040 - optimizelyconfig-csharp.md new file mode 100644 index 00000000..c9df3951 --- /dev/null +++ b/docs/docs/sdk-reference-guides/040 - optimizelyconfig-csharp.md @@ -0,0 +1,72 @@ +--- +title: "OptimizelyConfig" +slug: "optimizelyconfig-csharp" +hidden: false +createdAt: "2020-01-17T11:52:50.547Z" +updatedAt: "2020-01-28T21:53:11.290Z" +--- +[block:api-header] +{ + "title": "Overview" +} +[/block] +Full Stack SDKs open a well-defined set of public APIs, hiding all implementation details. However, some clients may need access to project configuration data within the "datafile". + +In this document, we extend our public APIs to define data models and access methods, which clients can use to access project configuration data. + +[block:api-header] +{ + "title": "OptimizelyConfig API" +} +[/block] + +A public configuration data model (OptimizelyConfig) is defined below as a structured format of static Optimizely Project data. + +OptimizelyConfig can be accessed from OptimizelyClient (top-level) with this public API call: +[block:code] +{ + "codes": [ + { + "code": "public OptimizelyConfig GetOptimizelyConfig()", + "language": "csharp" + } + ] +} +[/block] +`GetOptimizelyConfig` returns an `OptimizelyConfig` instance which include a datafile revision number, all experiments, and feature flags mapped by their key values. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "When the SDK datafile is updated (the client can add a notification listener for `OPTIMIZELY_CONFIG_UPDATE` to get notified), the client is expected to call the method to get the updated OptimizelyConfig data. See examples below." +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "// OptimizelyConfig is class describing the current project configuration data being used by this SDK instance.\n public class OptimizelyConfig\n {\n public string Revision { get; private set; }\n public IDictionary ExperimentsMap { get; private set; }\n public IDictionary FeaturesMap { get; private set; } \n }\n\n// Entity.IdKeyEntity is an abstract class used for inheritance in OptimizelyExperiment, OptimizelyFeature, OptimizelyVariation and OptimizelyVariable classes.\npublic abstract class IdKeyEntity : Entity, IEquatable\n{\n public string Id { get; set; }\n public string Key { get; set; }\n}\n\n// OptimizelyFeature is a class describing a feature and inherited from Entity.IdKeyEntity.\npublic class OptimizelyFeature : Entity.IdKeyEntity\n{\n public IDictionary ExperimentsMap { get; private set; }\n public IDictionary VariablesMap { get; private set; }\n}\n\n\n// OptimizelyExperiment is a class describing a feature test or an A/B test and inherited from Entity.IdKeyEntity.\npublic class OptimizelyExperiment : Entity.IdKeyEntity\n{\n public IDictionary VariationsMap { get; private set; }\n}\n\n\n// OptimizelyVariation is a class describing a variation in a feature test or A/B test and inherited from Entity.IdKeyEntity.\npublic class OptimizelyVariation : Entity.IdKeyEntity\n{\n [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n public bool? FeatureEnabled { get; private set; }\n public IDictionary VariablesMap { get; private set; }\n}\n\n\n// OptimizelyVariable is a class describing a feature variable and inherited from Entity.IdKeyEntity.\npublic class OptimizelyVariable : Entity.IdKeyEntity\n{\n public string Type { get; private set; }\n public string Value { get; private set; }\n}", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Examples" +} +[/block] +OptimizelyConfig can be accessed from OptimizelyClient (top-level) like this: + +[block:code] +{ + "codes": [ + { + "code": "var optimizelyConfig = optimizely.GetOptimizelyConfig();\n\n// all experiment keys\nvar experimentKeys = optimizelyConfig.ExperimentsMap.Keys;\nforeach(var experimentKey in experimentKeys) {\n // use experiment key data here.\n}\n\n// all experiments\nvar experiments = optimizelyConfig.ExperimentsMap.Values;\nforeach(var experiment in experiments) {\n // all variations\n var variations = experiment.VariationsMap.Values;\n foreach(var variation in variations) {\n var variables = variation.VariablesMap.Values;\n foreach(var variable in variables) {\n // use variable data here.\n }\n }\n}\n\n\n\n// all features\nvar features = optimizelyConfig.FeaturesMap.Values;\nforeach(var feature in features) {\n var experiments = feature.ExperimentsMap.Values;\n foreach(var experiment in experiments) {\n // use experiment data here.\n }\n}\n\n\n// listen to OPTIMIZELY_CONFIG_UPDATE to get updated data\nNotificationCenter.OptimizelyConfigUpdateCallback configUpdateListener = () => {\n var optimizelyConfig = optimizely.GetOptimizelyConfig();\n };\n optimizely.NotificationCenter.AddNotification(NotificationCenter.NotificationType.OptimizelyConfigUpdate, configUpdateListener);\n", + "language": "csharp" + } + ] +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/050 - implement-a-user-profile-csharp.md b/docs/docs/sdk-reference-guides/050 - implement-a-user-profile-csharp.md new file mode 100644 index 00000000..28a43f46 --- /dev/null +++ b/docs/docs/sdk-reference-guides/050 - implement-a-user-profile-csharp.md @@ -0,0 +1,51 @@ +--- +title: "Implement a user profile service" +slug: "implement-a-user-profile-csharp" +hidden: false +createdAt: "2019-09-12T13:43:49.170Z" +updatedAt: "2019-09-12T13:45:24.114Z" +--- +Use a **User Profile Service** to persist information about your users and ensure variation assignments are sticky. For example, if you are working on a backend website, you can create an implementation that reads and saves user profiles from a Redis or memcached store. + +In the C# SDK, there is no default implementation. Implementing a User Profile Service is optional and is only necessary if you want to keep variation assignments sticky even when experiment conditions are changed while it is running (for example, audiences, attributes, variation pausing, and traffic distribution). Otherwise, the C# SDK is stateless and rely on deterministic bucketing to return consistent assignments. See [How bucketing works](doc:how-bucketing-works) for more information. +[block:api-header] +{ + "title": "Implement a service" +} +[/block] +Refer to the code samples below to provide your own User Profile Service. It should expose two functions with the following signatures: + +* `lookup`: Takes a user ID string and returns a user profile matching the schema below. +* `save`: Takes a user profile and persists it. + +If you want to use the User Profile Service purely for tracking purposes and not sticky bucketing, you can implement only the `save` method (always return `nil` from `lookup`). +[block:code] +{ + "codes": [ + { + "code": "using System.Collections.Generic;\n\nusing OptimizelySDK;\nusing OptimizelySDK.Bucketing;\n\nclass InMemoryUserProfileService : UserProfileService\n{\n private Dictionary> userProfiles = new Dictionary>();\n Dictionary UserProfileService.Lookup(string userId)\n {\n // Retrieve and return user profile\n // Replace with userprofile variable\n return null;\n }\n\n void UserProfileService.Save(Dictionary userProfile)\n {\n // Save user profile\n }\n}\n\n\tvar optimizelyClient = new Optimizely(\n\t\tdatafile: datafile,\n userProfileService: userProfileService);\n\n", + "language": "csharp" + } + ] +} +[/block] +The code example below shows the JSON schema for the user profile object. + +Use `experiment_bucket_map` to override the default bucketing behavior and define an alternate experiment variation for a given user. For each experiment that you want to override, add an object to the map. Use the experiment ID as the key and include a `variation_id` property that specifies the desired variation. If there isn't an entry for an experiment, then the default bucketing behavior persists. + +In the example below, `^[a-zA-Z0-9]+$` is the experiment ID. +[block:code] +{ + "codes": [ + { + "code": "{\n \"title\": \"UserProfile\",\n \"type\": \"object\",\n \"properties\": {\n \"user_id\": {\"type\": \"string\"},\n \"experiment_bucket_map\": {\"type\": \"object\",\n \"patternProperties\": {\n \"^[a-zA-Z0-9]+$\": {\"type\": \"object\",\n \"properties\": {\"variation_id\": {\"type\":\"string\"}},\n \"required\": [\"variation_id\"]}\n }\n }\n },\n \"required\": [\"user_id\", \"experiment_bucket_map\"]\n}\n\n", + "language": "json" + } + ] +} +[/block] +The C# SDK uses the User Profile Service you provide to override Optimizely's default bucketing behavior in cases when an experiment assignment has been saved. + +When implementing your own User Profile Service, we recommend loading the user profiles into the User Profile Service on initialization and avoiding performing expensive, blocking lookups on the lookup function to minimize the performance impact of incorporating the service. + +When implementing in a multi-server or stateless environment, we suggest using this interface with a backend like [Cassandra](http://cassandra.apache.org/) or [Redis](https://redis.io/). You can decide how long you want to keep your sticky bucketing around by configuring these services. \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/060 - configure-event-dispatcher-csharp.md b/docs/docs/sdk-reference-guides/060 - configure-event-dispatcher-csharp.md new file mode 100644 index 00000000..512b1be4 --- /dev/null +++ b/docs/docs/sdk-reference-guides/060 - configure-event-dispatcher-csharp.md @@ -0,0 +1,30 @@ +--- +title: "Configure event dispatcher" +slug: "configure-event-dispatcher-csharp" +hidden: false +createdAt: "2019-09-12T13:43:55.726Z" +updatedAt: "2019-09-12T13:45:58.817Z" +--- +The Optimizely SDKs make HTTP requests for every impression or conversion that gets triggered. Each SDK has a built-in **event dispatcher** for handling these events, but we recommend overriding it based on the specifics of your environment. + +The C# SDK has an out-of-the-box asynchronous dispatcher. We recommend customizing the event dispatcher you use in production to ensure that you queue and send events in a manner that scales to the volumes handled by your application. Customizing the event dispatcher allows you to take advantage of features like batching, which makes it easier to handle large event volumes efficiently or to implement retry logic when a request fails. You can build your dispatcher from scratch or start with the provided dispatcher. + +The examples show that to customize the event dispatcher, initialize the Optimizely client (or manager) with an event dispatcher instance. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\nusing OptimizelySDK.Event.Dispatcher;\n\n// Create an Optimizely client with the default event dispatcher\n\tOptimizely OptimizelyClient = new Optimizely(\n\t\t\tdatafile: datafile,\n\t\t\teventDispatcher: new DefaultEventDispatcher(new OptimizelySDK.Logger.DefaultLogger()));\n\n", + "language": "csharp" + } + ] +} +[/block] +The event dispatcher should implement a `dispatchEvent` function, which takes in three arguments: `httpVerb`, `url`, and `params`, all of which are created by the internal `EventBuilder` class. In this function, you should send a `POST` request to the given `url` using the `params` as the body of the request (be sure to stringify it to JSON) and `{content-type: 'application/json'}` in the headers. +[block:callout] +{ + "type": "warning", + "title": "Important", + "body": "If you are using a custom event dispatcher, do not modify the event payload returned from Optimizely. Modifying this payload will alter your results." +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/070 - event-batching-csharp.md b/docs/docs/sdk-reference-guides/070 - event-batching-csharp.md new file mode 100644 index 00000000..f1539996 --- /dev/null +++ b/docs/docs/sdk-reference-guides/070 - event-batching-csharp.md @@ -0,0 +1,183 @@ +--- +title: "Event batching" +slug: "event-batching-csharp" +hidden: false +createdAt: "2019-09-12T13:44:04.059Z" +updatedAt: "2019-12-13T00:25:39.892Z" +--- +The [Optimizely Full Stack C# SDK](https://github.com/optimizely/csharp-sdk) now batches impression and conversion events into a single payload before sending it to Optimizely. This is achieved through a new SDK component called the event processor. + +Event batching has the advantage of reducing the number of outbound requests to Optimizely depending on how you define, configure, and use the event processor. It means less network traffic for the same number of Impression and conversion events tracked. + +In the C# SDK, `BatchEventProcessor` provides implementation of the `EventProcessor` interface and batches events. You can control batching based on two parameters: + +- Batch size: Defines the number of events that are batched together before sending to Optimizely. +- Flush interval: Defines the amount of time after which any batched events should be sent to Optimizely. + +An event consisting of the batched payload is sent as soon as the batch size reaches the specified limit or flush interval reaches the specified time limit. `BatchEventProcessor` options are described in more detail below. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "Event batching works with both out-of-the-box and custom event dispatchers.\n\nThe event batching process doesn't remove any personally identifiable information (PII) from events. You must still ensure that you aren't sending any unnecessary PII to Optimizely." +} +[/block] + +[block:api-header] +{ + "title": "Basic example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\n\nclass App\n{\n static void Main(string[] args)\n {\n string sdkKey = args[0];\n // Returns Optimizely Client\n OptimizelyFactory.NewDefaultInstance(sdkKey);\n }\n}", + "language": "csharp" + } + ] +} +[/block] +By default, batch size is 10 and flush interval is 30 seconds. +[block:api-header] +{ + "title": "Advanced Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\n\nclass App\n{\n static void Main(string[] args)\n {\n string sdkKey = args[0];\n ProjectConfigManager projectConfigManager = HttpProjectConfigManager.builder()\n .WithSdkKey(sdkKey)\n .Build();\n\n BatchEventProcessor batchEventProcessor = new BatchEventProcessor.Builder()\n .WithMaxBatchSize(10)\n .WithFlushInterval(TimeSpan.FromSeconds(30))\n .Build();\n\n Optimizely optimizely = new Optimizely(\n projectConfigManager,\n .. // Other Params\n ..batchEventProcessor\n );\n }\n}", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "BatchEventProcessor" +} +[/block] +`BatchEventProcessor` is an implementation of `EventProcessor` where events are batched. The class maintains a single consumer thread that pulls events off of the `BlockingCollection` and buffers them for either a configured batch size or a maximum duration before the resulting `LogEvent` is sent to the `EventDispatcher` and `NotificationCenter`. + +The following properties can be used to customize the BatchEventProcessor configuration *using the Builder class* +[block:parameters] +{ + "data": { + "h-0": "Property", + "h-1": "Default value", + "0-0": "**EventDispatcher**", + "0-1": "DefautEventDispatcher", + "1-1": "10", + "1-0": "**BatchSize**", + "h-2": "Description", + "h-3": "Server", + "0-2": "Used to dispatch event payload to Optimizely.", + "1-2": "The maximum number of events to batch before dispatching. Once this number is reached, all queued events are flushed and sent to Optimizely.", + "0-3": "Based on your organization's requirements.", + "1-3": "Based on your organization's requirements.", + "3-0": "**EventQueue**", + "3-1": "1000", + "3-2": "BlockingCollection that queues individual events to be batched and dispatched by the executor.", + "2-0": "**FlushInterval**", + "2-1": "30000 (30 Seconds)", + "2-2": "Milliseconds to wait before batching and dispatching events.", + "4-0": "**NotificationCenter**", + "4-1": "null", + "4-2": "Notification center instance to be used to trigger any notifications." + }, + "cols": 3, + "rows": 5 +} +[/block] +For more information, see [Initialize SDK](doc:initialize-sdk-csharp). +[block:api-header] +{ + "title": "Side effects" +} +[/block] +The table lists other Optimizely functionality that may be triggered by using this class. +[block:parameters] +{ + "data": { + "h-0": "Functionality", + "h-1": "Description", + "0-1": "Whenever the event processor produces a batch of events, a LogEvent object will be created using the EventFactory.\nIt contains batch of conversion and impression events. \nThis object will be dispatched using the provided event dispatcher and also it will be sent to the notification subscribers.", + "1-1": "Flush invokes the LOGEVENT [notification listener](doc:set-up-notification-listener-csharp) if this listener is subscribed to.", + "1-0": "Notification Listeners", + "0-0": "[LogEvent](https://staging-optimizely-parent.readme.io/staging-optimizely-full-stack/docs/logevent-c#)" + }, + "cols": 2, + "rows": 2 +} +[/block] +### Registering LogEvent listener + +To register a LogEvent listener +[block:code] +{ + "codes": [ + { + "code": "NotificationCenter.AddNotification(\n \t\t\t\t\t\t\t\t\t\tNotificationType.LogEvent, \n \t new LogEventCallback((logevent) => {\n \t\t // Your code here\n \t\t\t\t\t})\n );", + "language": "csharp" + } + ] +} +[/block] +### LogEvent + +LogEvent object gets created using [EventFactory](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Event/EventFactory.cs).It represents the batch of impression and conversion events we send to the Optimizely backend. +[block:parameters] +{ + "data": { + "h-0": "Object", + "h-1": "Type", + "h-2": "Description", + "0-0": "**Url**\nRequired", + "0-1": " string ", + "0-2": "URL to dispatch log event to.", + "1-2": "Parameters to be set in the log event. It contains [EventBatch](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Event/Entity/EventBatch.cs) of all UserEvents inside [Visitors](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Event/Entity/EventBatch.cs#L45).", + "2-2": "The HTTP verb to use when dispatching the log event. It can be GET or POST.", + "3-2": "Headers to be set when sending the request.", + "3-0": "**Headers** ", + "2-0": "**HttpVerb**\nRequired", + "1-0": "**Params**\nRequired", + "1-1": "Dictionary", + "3-1": "Dictionary Headers", + "2-1": "string" + }, + "cols": 3, + "rows": 4 +} +[/block] + +[block:api-header] +{ + "title": "Dispose Optimizely on application exit" +} +[/block] +If you enable event batching, it's important that you call the Close method (`optimizely.Dispose()`) prior to exiting. This ensures that queued events are flushed as soon as possible to avoid any data loss. +[block:callout] +{ + "type": "warning", + "title": "Important", + "body": "Because the Optimizely client maintains a buffer of queued events, we recommend that you call `Dispose()` on the Optimizely instance before shutting down your application or whenever dereferencing the instance." +} +[/block] + +[block:parameters] +{ + "data": { + "0-0": "**Dispose()**", + "h-0": "Method", + "h-1": "Description", + "0-1": "Stops all timers and flushes the event queue. This method will also stop any timers that are happening for the data-file manager." + }, + "cols": 2, + "rows": 1 +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/080 - customize-logger-csharp.md b/docs/docs/sdk-reference-guides/080 - customize-logger-csharp.md new file mode 100644 index 00000000..02134627 --- /dev/null +++ b/docs/docs/sdk-reference-guides/080 - customize-logger-csharp.md @@ -0,0 +1,45 @@ +--- +title: "Customize logger" +slug: "customize-logger-csharp" +hidden: false +createdAt: "2019-09-12T13:44:11.768Z" +updatedAt: "2019-09-12T13:47:56.603Z" +--- +The **logger** logs information about your experiments to help you with debugging. You can customize where log information is sent and what kind of information is tracked. + +To improve your experience setting up the SDK and configuring your production environment, we recommend that you pass in a logger for your Optimizely client. See the code example below. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK.Logger;\n\n/**\n * Log a message at a certain level.\n * - Parameter level: The priority level of the log.\n * - Parameter message: The message to log.\n **/\npublic class CustomLogger : ILogger\n{\n private LogLevel MinLogLevel;\n\n public CustomLogger(LogLevel minLogLevel)\n {\n this.MinLogLevel = minLogLevel;\n }\n public void Log(LogLevel level, string message)\n {\n if (MinLogLevel <= level) {\n switch (level) {\n case LogLevel.DEBUG:\n // DEBUG log message\n break;\n case LogLevel.INFO:\n // INFO log message\n break;\n case LogLevel.WARN:\n // WARNING log message\n break;\n case LogLevel.ERROR:\n // ERROR log message\n break;\n }\n }\n }\n}\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Log levels" +} +[/block] +The table below lists the log levels for the C# SDK. +[block:parameters] +{ + "data": { + "h-0": "Log Level", + "h-1": "Explanation", + "0-0": "**OptimizelySDK.Logger.LogLevel.ERROR**", + "0-1": "Events that prevent feature flags from functioning correctly (for example, invalid datafile in initialization and invalid feature keys) are logged. The user can take action to correct.", + "1-0": "**OptimizelySDK.Logger.LogLevel.WARN**", + "1-1": "Events that don't prevent feature flags from functioning correctly, but can have unexpected outcomes (for example, future API deprecation, logger or error handler are not set properly, and nil values from getters) are logged.", + "2-0": "**OptimizelySDK.Logger.LogLevel.INFO**", + "2-1": "Events of significance (for example, activate started, activate succeeded, tracking started, and tracking succeeded) are logged. This is helpful in showing the lifecycle of an API call.", + "3-1": "Any information related to errors that can help us debug the issue (for example, the feature flag is not running, user is not included in the rollout) are logged.", + "3-0": "**OptimizelySDK.Logger.LogLevel.DEBUG**" + }, + "cols": 2, + "rows": 4 +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/090 - customize-error-handler-csharp.md b/docs/docs/sdk-reference-guides/090 - customize-error-handler-csharp.md new file mode 100644 index 00000000..56c0d45a --- /dev/null +++ b/docs/docs/sdk-reference-guides/090 - customize-error-handler-csharp.md @@ -0,0 +1,22 @@ +--- +title: "Customize error handler" +slug: "customize-error-handler-csharp" +hidden: false +createdAt: "2019-09-12T13:44:18.412Z" +updatedAt: "2019-09-12T13:48:38.080Z" +--- +You can provide your own custom **error handler** logic to standardize across your production environment. + +This error handler is called when SDK is not executed as expected, it may be because of arguments provided to the SDK or running in an environment where network or any other disruptions occur. + +See the code example below. If the error handler is not overridden, a no-op error handler is used by default. +[block:code] +{ + "codes": [ + { + "code": "using System;\nusing OptimizelySDK.ErrorHandler;\n\n/**\n * Creates a CustomErrorHandler and calls HandleError when exception is raised by the SDK. \n **/\n/** CustomErrorHandler should be inherited by IErrorHandler, namespace of OptimizelySDK.ErrorHandler.\n **/\npublic class CustomErrorHandler : IErrorHandler\n{\n /// \n /// Handle exceptions when raised by the SDK.\n /// \n /// object of Exception raised by the SDK.\n public void HandleError(Exception exception)\n {\n throw new NotImplementedException();\n }\n}\n\n", + "language": "csharp" + } + ] +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/100 - pass-in-audience-attributes-csharp.md b/docs/docs/sdk-reference-guides/100 - pass-in-audience-attributes-csharp.md new file mode 100644 index 00000000..858f4f4a --- /dev/null +++ b/docs/docs/sdk-reference-guides/100 - pass-in-audience-attributes-csharp.md @@ -0,0 +1,26 @@ +--- +title: "Pass in audience attributes" +slug: "pass-in-audience-attributes-csharp" +hidden: false +createdAt: "2019-09-12T13:44:29.855Z" +updatedAt: "2019-09-12T20:30:05.154Z" +--- +You can pass strings, numbers, Booleans, and null as user attribute values. The example below shows how to pass in attributes. +[block:code] +{ + "codes": [ + { + "code": "UserAttributes attributes = new UserAttributes\n{\n { \"DEVICE\", \"iPhone\" },\n { \"AD_SOURCE\", \"my_campaign\" }\n};\n\nbool enabled = OptimizelyClient.IsFeatureEnabled(\"new_feature\", \"user123\", attributes);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:callout] +{ + "type": "warning", + "title": "Important", + "body": "During audience evaluation, note that if you don't pass a valid attribute value for a given audience condition—for example, if you pass a string when the audience condition requires a Boolean, or if you simply forget to pass a value—then that condition will be skipped. The [SDK logs](doc:customize-logger-csharp) will include warnings when this occurs." +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/110 - set-up-notification-listener-csharp.md b/docs/docs/sdk-reference-guides/110 - set-up-notification-listener-csharp.md new file mode 100644 index 00000000..eb8ecfd3 --- /dev/null +++ b/docs/docs/sdk-reference-guides/110 - set-up-notification-listener-csharp.md @@ -0,0 +1,84 @@ +--- +title: "Set up notification listener" +slug: "set-up-notification-listener-csharp" +hidden: false +createdAt: "2019-09-12T13:44:24.921Z" +updatedAt: "2020-02-10T19:46:50.861Z" +--- +Notification listeners trigger a callback function that you define when certain actions are triggered in the SDK. + +The most common use case is to send a stream of all feature flag decisions to an analytics provider or to an internal data warehouse to join it with other data that you have about your users. + +To track feature usage: +1. Sign up for an analytics provider of your choice (for example, Segment) +2. Set up a notification listener of type 'DECISION' +3. Follow your analytics provider's documentation and send events from within the decision listener callback + +Steps 1 and 3 aren't covered in this documentation. However, setting up a 'DECISION' notification listener is covered below. +[block:api-header] +{ + "title": "Set up a DECISION notification listener" +} +[/block] +The `DECISION` notification listener enables you to be notified whenever the SDK determines what decision value to return for a feature. The callback is triggered with the decision type, associated decision information, user ID, and attributes. + +`DECISION` listeners are triggered in multiple cases. Please see the tables at the end of this section for complete detail. + +To set up a `DECISION` listener: + 1. Define a callback to be called when the `DECISION` event is triggered + 2. Add the callback to the notification center on the Optimizely instance + +The example code below shows how to add a listener, remove a listener, remove all listeners of a specific type (such as all decision listeners), and remove all listeners. +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK;\nusing OptimizelySDK.Entity;\nusing OptimizelySDK.Event;\nusing OptimizelySDK.Notifications;\nusing NotificationType = OptimizelySDK.Notifications.NotificationCenter.NotificationType;\n\nvar optimizelyClient = new Optimizely(datafile);\n\n\nNotificationCenter.DecisionCallback OnDecision = (type, userId, userAttributes, decisionInfo) =>\n{\n if (type == \"feature\")\n {\n Console.WriteLine(string.Format(\"Feature access related information: {0}\", decisionInfo.ToString()));\n // Send data to analytics provider here\n }\n};\n\n// Add a Decision notification listener\nint decisionListenerId = optimizely.NotificationCenter.AddNotification(NotificationType.Decision, OnDecision);\n\n// Remove notification listener\noptimizelyClient.NotificationCenter.RemoveNotification(decisionListenerId);\n\n// Clear all notification listeners of a certain type\noptimizelyClient.NotificationCenter.ClearNotifications(NotificationType.Decision);\n\n// Clear all notifications\noptimizelyClient.NotificationCenter.ClearAllNotifications();\n\n", + "language": "csharp" + } + ] +} +[/block] +The tables below show the information provided to the listener when it is triggered. +[block:parameters] +{ + "data": { + "0-0": "**type**", + "0-1": "string", + "0-2": "- `feature`: Returned when you use the Is Feature Enabled to determine if user has access to one specific feature, or Get Enabled Features method to determine if user has access to multiple features.\n\n- `ab-test`: Returned when you use activate or get_variation to determine the variation for a user, and the given experiment is not associated to any feature.\n\n- `feature-test`: Returned when you use activate or get_variation to determine the variation for a user, and the given experiment is associated to some feature.\n\n- `feature-variable`: Returned when you use one of the get_feature_variable methods to determine value of some feature variable. Such as get_feature_variable_boolean.", + "1-0": "**decision info**", + "1-1": "map", + "2-0": "**user ID**", + "2-1": "string", + "3-0": "**attributes**", + "3-1": "map", + "1-2": "Key-value map that consists of data corresponding to the decision and based on the `type`.\nSee the table below for valid fields and values for each `type`.", + "h-0": "Field", + "h-1": "Type", + "h-2": "Description", + "2-2": "The user ID.", + "3-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting. Non-string values are only supported in the 3.0 SDK and above." + }, + "cols": 3, + "rows": 4 +} +[/block] + +[block:parameters] +{ + "data": { + "0-1": "- `featureKey`: String id of the feature.\n- `featureEnabled`: True or false based on whether the feature is enabled for the user.\n- `source`: String denoting how user gained access to the feature. Value is:\n - `feature-test` if the feature became enabled or disabled for the user because of some experiment associated with the feature.\n - `rollout` if the feature became enabled or disabled for the user because of the rollout configuration associated with the feature.\n- `sourceInfo`: Empty if the source is rollout. Holds experimentKey and variationKey if the source is feature-test.", + "0-0": "**feature**", + "h-0": "Type", + "h-1": "Decision Info Values", + "1-1": "- `experimentKey`: String key of the experiment\n- `variationKey`: String key of the variation to which the user got bucketed.", + "2-1": "- `experimentKey`: String key of the experiment\n- `variationKey`: String key of the variation to which the user got bucketed.", + "3-1": "- `featureKey`: String id of the feature.\n- `featureEnabled`: True or false based on whether the feature is enabled for the user.\n- `source`: String denoting how user gained access to the feature. Value is:\n - `feature-test` if the feature became enabled or disabled for the user because of some experiment associated with the feature.\n - `rollout` if the feature became enabled or disabled for the user because of the rollout configuration associated with the feature.\n- `variableKey`: String key of the feature variable.\n- `variableValue`: Mixed value of the feature variable for this user.\n- `variableType`: String type of the feature variable. Can be one of boolean, double, integer, string.\n- `sourceInfo`: Map denoting source of decision. Empty if the source is rollout. Holds experimentKey and variationKey if the source is feature-test.", + "3-0": "**feature-variable**", + "2-0": "**feature-test**", + "1-0": "**ab-test**" + }, + "cols": 2, + "rows": 4 +} +[/block] \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/120 - activate-csharp.md b/docs/docs/sdk-reference-guides/120 - activate-csharp.md new file mode 100644 index 00000000..69dbe29d --- /dev/null +++ b/docs/docs/sdk-reference-guides/120 - activate-csharp.md @@ -0,0 +1,142 @@ +--- +title: "Activate" +slug: "activate-csharp" +hidden: false +createdAt: "2019-09-12T13:51:40.641Z" +updatedAt: "2019-09-12T20:31:54.396Z" +--- +Activates an A/B test for the specified user to start an experiment: determines whether they qualify for the experiment, buckets a qualified user into a variation, and sends an impression event to Optimizely. +[block:api-header] +{ + "title": "Version" +} +[/block] +3.1.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +This method requires an experiment key, user ID, and (optionally) attributes. The experiment key must match the experiment key you created when you set up the experiment in the Optimizely app. The user ID string uniquely identifies the participant in the experiment. + +If the user qualifies for the experiment, the method returns the variation key that was chosen. If the user was not eligible—for example, because the experiment was not running in this environment or the user didn't match the targeting attributes and audience conditions—then the method returns null. + +Activate respects the configuration of the experiment specified in the datafile. The method: + * Evaluates the user attributes for audience targeting. + * Includes the user attributes in the impression event to support [results segmentation](doc:analyze-results#section-segment-results). + * Hashes the user ID or bucketing ID to apply traffic allocation. + * Respects forced bucketing and whitelisting. + * Triggers an impression event if the user qualifies for the experiment. + +Activate also respects customization of the SDK client. Throughout this process, this method: + * Logs its decisions via the logger. + * Triggers impressions via the event dispatcher. + * Raises errors via the error handler. + * Remembers variation assignments via the User Profile Service. + * Alerts notification listeners, as applicable. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "For more information on how the variation is chosen, see [How bucketing works](how-bucketing-works)." +} +[/block] + +[block:api-header] +{ + "title": "Parameters" +} +[/block] +The parameter names for C# are listed below. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**experiment key**\n*required*", + "0-1": "string", + "1-0": "**user ID**\n*required*", + "1-1": "string", + "0-2": "The experiment to activate.", + "1-2": "The user ID.", + "2-0": "**userAttributes**\n*optional*", + "2-1": "map", + "2-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting and results segmentation. Non-string values are only supported in the 3.0 SDK and above." + }, + "cols": 3, + "rows": 3 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +null|Variation Representing variation +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK.Entity;\n\nvar attributes = new UserAttributes {\n { \"device\", \"iPhone\" },\n { \"lifetime\", 24738388 },\n { \"is_logged_in\", true },\n};\n\nvar variation = optimizelyClient.Activate(\"my_experiment_key\", \"user_123\", attributes);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Side effects" +} +[/block] +The table lists other other Optimizely functionality that may be triggered by using this method. +[block:parameters] +{ + "data": { + "h-0": "Functionality", + "h-1": "Description", + "0-0": "Impressions", + "0-1": "Accessing this method triggers an impression if the user is included in an active A/B test. \n\nSee [Implement impressions](doc:implement-impressions) for guidance on when to use Activate versus [Get Variation](doc:get-variation-csharp).", + "1-0": "Notification Listeners", + "1-1": "In SDKs v3.0 and earlier: Activate invokes the `ACTIVATE` [notification listener](doc:set-up-notification-listener-csharp) if the user is included in an active A/B test.\n\nIn SDKs v3.1 and later: Invokes the `DECISION` notification listener if this listener is enabled." + }, + "cols": 2, + "rows": 2 +} +[/block] + +[block:api-header] +{ + "title": "Notes" +} +[/block] +### Activate versus Get Variation +Use Activate when the visitor actually sees the experiment. Use Get Variation when you need to know which bucket a visitor is in before showing the visitor the experiment. Impressions are tracked by [Is Feature Enabled](doc:is-feature-enabled) when there is a feature test running on the feature and the visitor qualifies for that feature test. + +For example, suppose you want your web server to show a visitor variation_1 but don't want the visitor to count until they open a feature that isn't visible when the variation loads, like a modal. In this case, use Get Variation in the backend to specify that your web server should respond with variation_1, and use Activate in the front end when the visitor sees the experiment. + +Also, use Get Variation when you're trying to align your Optimizely results with client-side third-party analytics. In this case, use Get Variation to retrieve the variation—and even show it to the visitor—but only call Activate when the analytics call goes out. + +See [Implement impressions](doc:implement-impressions) for more information about whether to use Activate or Get Variation for a call. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "Conversion events can only be attributed to experiments with previously tracked impressions. Impressions are tracked by Activate, not by Get Variation. As a general rule, Optimizely impressions are required for experiment results and not only for billing." +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/130 - get-enabled-features-csharp.md b/docs/docs/sdk-reference-guides/130 - get-enabled-features-csharp.md new file mode 100644 index 00000000..432f0cb5 --- /dev/null +++ b/docs/docs/sdk-reference-guides/130 - get-enabled-features-csharp.md @@ -0,0 +1,75 @@ +--- +title: "Get Enabled Features" +slug: "get-enabled-features-csharp" +hidden: false +createdAt: "2019-09-12T13:53:46.225Z" +updatedAt: "2019-09-12T20:33:07.026Z" +--- +Retrieves a list of features that are enabled for the user. Invoking this method is equivalent to running [Is Feature Enabled](doc:is-feature-enabled-csharp) for each feature in the datafile sequentially. + +This method takes into account the user `attributes` passed in, to determine if the user is part of the audience that qualifies for the experiment. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0 +[block:api-header] +{ + "title": "Description" +} +[/block] +This method iterates through all feature flags and for each feature flag invokes [Is Feature Enabled](doc:is-feature-enabled-csharp). If a feature is enabled, this method adds the feature’s key to the return list. +[block:api-header] +{ + "title": "Parameters" +} +[/block] +The table below lists the required and optional parameters in C#. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**userId**\n*required*", + "0-1": "string", + "1-0": "**userAttributes**\n*optional*", + "1-1": "map", + "0-2": "The ID of the user to check.", + "1-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting. Non-string values are only supported in the 3.0 SDK and above." + }, + "cols": 3, + "rows": 2 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +A list of the feature keys that are enabled for the user, or an empty list if no features could be found for the specified user. +[block:api-header] +{ + "title": "Examples" +} +[/block] +This section shows a simple example of how you can use the method. +[block:code] +{ + "codes": [ + { + "code": "var actualFeaturesList = OptimizelyMock.Object.GetEnabledFeatures(TestUserId, userAttributes);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/140 - get-feature-variable-csharp.md b/docs/docs/sdk-reference-guides/140 - get-feature-variable-csharp.md new file mode 100644 index 00000000..8e14a3fd --- /dev/null +++ b/docs/docs/sdk-reference-guides/140 - get-feature-variable-csharp.md @@ -0,0 +1,159 @@ +--- +title: "Get Feature Variable" +slug: "get-feature-variable-csharp" +hidden: false +createdAt: "2019-09-12T13:51:45.265Z" +updatedAt: "2019-09-16T23:48:13.270Z" +--- +Evaluates the specified feature variable of a specific variable type and returns its value. + +This method is used to evaluate and return a feature variable. Multiple versions of this method are available and are named according to the data type they return: + * [Boolean](#section-boolean) + * [Double](#section-double) + * [Integer](#section-integer) + * [String](#section-string) + +This method takes into account the user `attributes` passed in, to determine if the user is part of the audience that qualifies for the experiment. + +### Boolean + +Returns the value of the specified Boolean variable. +[block:code] +{ + "codes": [ + { + "code": "public bool? GetFeatureVariableBoolean(string featureKey, string variableKey, string userId, UserAttributes userAttributes = null)\n \n ", + "language": "csharp" + } + ] +} +[/block] +### Double + +Returns the value of the specified double variable. +[block:code] +{ + "codes": [ + { + "code": "public double? GetFeatureVariableDouble(string featureKey, string variableKey, string userId, UserAttributes userAttributes = null)\n \n ", + "language": "csharp" + } + ] +} +[/block] +### Integer + +Returns the value of the specified integer variable. +[block:code] +{ + "codes": [ + { + "code": "public int? GetFeatureVariableInteger(string featureKey, string variableKey, string userId, UserAttributes userAttributes = null)\n \n ", + "language": "csharp" + } + ] +} +[/block] +### String + +Returns the value of the specified string variable. +[block:code] +{ + "codes": [ + { + "code": "public string GetFeatureVariableString(string featureKey, string variableKey, string userId, UserAttributes userAttributes = null)\n \n ", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0, v3.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +Each of the Get Feature Variable methods follows the same logic as [Is Feature Enabled](doc:is-feature-enabled-csharp): +1. Evaluate any feature tests running for a user. +2. Check the default configuration on a rollout. + +The default value is returned if neither of these are applicable for the specified user, or if the user is in a variation where the feature is disabled. +[block:callout] +{ + "type": "warning", + "title": "Important", + "body": "Unlike [Is Feature Enabled](doc:is-feature-enabled-csharp), the Get Feature Variable methods do not trigger an impression event. This means that if you're running a feature test, events won't be counted until you call Is Feature Enabled. If you don't call Is Feature Enabled, you won't see any visitors on your results page." +} +[/block] + +[block:api-header] +{ + "title": "Parameters" +} +[/block] +Required and optional parameters in C# are listed below. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**featureKey**\n*required*", + "0-1": "string", + "1-0": "**variableKey**\n*required*", + "1-1": "string", + "0-2": "The feature key is defined from the Features dashboard.", + "1-2": "The key that identifies the feature variable.", + "2-0": "**userId**\n*required*", + "3-0": "**userAttributes**\n*required*", + "2-1": "string", + "3-1": "map", + "2-2": "The user ID string uniquely identifies the participant in the experiment.", + "3-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting and results segmentation. Non-string values are only supported in the 3.0 SDK and above." + }, + "cols": 3, + "rows": 4 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +Feature variable value or null +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK.Entity;\n\nvar attributes = new UserAttributes {\n { \"device\", \"iPhone\" },\n { \"lifetime\", 24738388 },\n { \"is_logged_in\", true },\n};\n\nvar featureVariableValue = optimizelyClient.GetFeatureVariableDouble(\"my_feature_key\", \"double_variable_key\", \"user_123\", attributes);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Side effects" +} +[/block] +In SDKs v3.1 and later: Invokes the `DECISION` [notification listener](doc:set-up-notification-listener-csharp) if this listener is enabled. +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/150 - get-forced-variation-csharp.md b/docs/docs/sdk-reference-guides/150 - get-forced-variation-csharp.md new file mode 100644 index 00000000..bad6a204 --- /dev/null +++ b/docs/docs/sdk-reference-guides/150 - get-forced-variation-csharp.md @@ -0,0 +1,83 @@ +--- +title: "Get Forced Variation" +slug: "get-forced-variation-csharp" +hidden: false +createdAt: "2019-09-12T13:51:51.169Z" +updatedAt: "2019-09-12T20:34:30.074Z" +--- +Returns the forced variation set by [Set Forced Variation](doc:set-forced-variation-csharp), or `null` if no variation was forced. + +A user can be forced into a variation for a given experiment for the lifetime of the Optimizely client. This method gets the variation that the user has been forced into. The forced variation value is runtime only and does not persist across application launches. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0, v3.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +Forced bucketing variations take precedence over whitelisted variations, variations saved in a User Profile Service (if one exists), and the normal bucketed variation. Variations are overwritten when [Set Forced Variation](doc:set-forced-variation-csharp) is invoked. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "A forced variation only persists for the lifetime of an Optimizely client." +} +[/block] + +[block:api-header] +{ + "title": "Parameters" +} +[/block] +This table lists the required and optional parameters for the C# SDK. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**experimentKey**\n*required*", + "0-1": "string", + "1-0": "**userId**\n*required*", + "1-1": "string", + "0-2": "The key of the experiment to retrieve the forced variation.", + "1-2": "The ID of the user in the forced variation." + }, + "cols": 3, + "rows": 2 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +null|string The variation key. +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "var variation = optimizelyClient.GetForcedVariation(“my_experiment_key”, “user_123”);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/160 - get-variation-csharp.md b/docs/docs/sdk-reference-guides/160 - get-variation-csharp.md new file mode 100644 index 00000000..491743ed --- /dev/null +++ b/docs/docs/sdk-reference-guides/160 - get-variation-csharp.md @@ -0,0 +1,228 @@ +--- +title: "Get Variation" +slug: "get-variation-csharp" +hidden: false +createdAt: "2019-09-12T13:51:54.916Z" +updatedAt: "2019-09-12T20:35:30.799Z" +--- +Returns a variation where the visitor will be bucketed, without triggering an impression. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0, v3.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +Takes the same arguments and returns the same values as [Activate](doc:activate-csharp), but without sending an impression network request. The behavior of the two methods is identical otherwise. + +Use Get Variation if Activate has been called and the current variation assignment is needed for a given experiment and user. This method bypasses redundant network requests to Optimizely. + +See [Implement impressions](doc:implement-impressions) for guidance on when to use each method. +[block:api-header] +{ + "title": "Parameters" +} +[/block] +This table lists the required and optional parameters for the C# SDK. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**experimentey**\n*required*", + "0-1": "string", + "1-0": "**userID**\n*required*", + "1-1": "string", + "0-2": "The key of the experiment.", + "1-2": "The ID of the user.", + "2-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting and results segmentation. Non-string values are only supported in the 3.0 SDK and above.", + "2-1": "map", + "2-0": "**attributes**\n*optional*" + }, + "cols": 3, + "rows": 3 +} +[/block] +The specific parameter names for each supported language are as follows: +[block:code] +{ + "codes": [ + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "Android" + }, + { + "code": "experimentKey\nuserId\nuserAttributes\n\n", + "language": "text", + "name": "C#" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "Java" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "JavaScript" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "Node" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "Objective-C" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "php", + "name": "PHP" + }, + { + "code": "experiment_key\nuser_id\nattributes\n\n", + "language": "text", + "name": "Python" + }, + { + "code": "experiment_key\nuser_id\nattributes\n\n", + "language": "text", + "name": "Ruby" + }, + { + "code": "experimentKey\nuserId\nattributes\n\n", + "language": "text", + "name": "Swift" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +The table below lists the specific information returned for each supported language. +[block:parameters] +{ + "data": { + "h-0": "Language", + "h-1": "Return", + "0-0": "Android", + "0-1": "@return the variation for the provided experiment key, user id, and attributes", + "1-1": "null|Variation Representing variation", + "1-0": "C#", + "2-1": "@return the variation for the provided experiment key, user id, and attributes", + "2-0": "Java", + "3-0": "JavaScript (browser)", + "4-0": "JavaScript (Node)", + "5-0": "Objective-C", + "6-0": "PHP", + "7-0": "Python", + "8-0": "Ruby", + "9-0": "Swift", + "3-1": "@return {string|null} variation key", + "4-1": "@return {string|null} variation key", + "5-1": "@return The variation into which the user is bucketed. This value can be nil.", + "6-1": "@return null|string Representing the variation key", + "7-1": "Returns: Variation key representing the variation in which the user will be bucketed. None if user is not in experiment or if experiment is not running.", + "8-1": "@return [variation key] where visitor will be bucketed.\n@return [nil] if the experiment is not running, if the user is not in the experiment, or if the datafile is invalid.", + "9-1": "@return The variation into which the user was bucketed. This value can be nil." + }, + "cols": 2, + "rows": 10 +} +[/block] + +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "import com.optimizely.ab.config.Variation;\n\nMap attributes = new HashMap<>();\nattributes.put(\"device\", \"iPhone\");\nattributes.put(\"lifetime\", 24738388);\nattributes.put(\"is_logged_in\", true);\n\nVariation variation = optimizelyClient.getVariation(\"my_experiment_key\", \"user_123\", attributes);\n\n", + "language": "java", + "name": "Android" + }, + { + "code": "using OptimizelySDK.Entity;\n\nvar attributes = new UserAttributes {\n { \"device\", \"iPhone\" },\n { \"lifetime\", 24738388 },\n { \"is_logged_in\", true },\n};\n\nvar variation = optimizelyClient.GetVariation(\"my_experiment_key\", \"user_123\", attributes);\n\n", + "language": "csharp" + }, + { + "code": "import com.optimizely.ab.config.Variation;\n\nMap attributes = new HashMap<>();\nattributes.put(\"device\", \"iPhone\");\nattributes.put(\"lifetime\", 24738388);\nattributes.put(\"is_logged_in\", true);\n\nVariation variation = optimizelyClient.getVariation(\"my_experiment_key\", \"user_123\", attributes);\n\n", + "language": "java" + }, + { + "code": "var attributes = {\n device: 'iPhone',\n lifetime: 24738388,\n is_logged_in: true,\n};\n\nvar variationKey = optimizelyClient.getVariation('my_experiment_key', 'user_123', attributes);\n\n", + "language": "javascript" + }, + { + "code": "var attributes = {\n device: 'iPhone',\n lifetime: 24738388,\n is_logged_in: true,\n};\n\nvar variationKey = optimizelyClient.getVariation('my_experiment_key', 'user_123', attributes);\n\n", + "language": "javascript", + "name": "Node" + }, + { + "code": "NSDictionary *attributes = @{\n @\"device\": @\"iPhone\",\n @\"lifetime\": @24738388,\n @\"is_logged_in\": @true\n};\n\nNSString *variationKey = [optimizely getVariationKeyWithExperimentKey: @\"my_experiment_key\" \n userId:@\"user_123\"\n attributes:attributes\n error:nil];\n\n\n", + "language": "objectivec" + }, + { + "code": "$attributes = [\n 'device' => 'iPhone',\n 'lifetime' => 24738388,\n 'is_logged_in' => true\n];\n\n$variationKey = $optimizelyClient->getVariation('my_experiment_key', 'user_123', $attributes);\n\n", + "language": "php" + }, + { + "code": "attributes = {\n 'device': 'iPhone',\n 'lifetime': 24738388,\n 'is_logged_in': True,\n}\n\nvariation_key = optimizely_client.get_variation('my_experiment_key', 'user_123', attributes)\n\n", + "language": "python" + }, + { + "code": "attributes = {\n 'device' => 'iPhone',\n 'lifetime' => 24738388,\n 'is_logged_in' => true,\n}\n\nvariation_key = optimizely_client.get_variation('my_experiment_key', 'user_123', attributes)\n\n", + "language": "ruby" + }, + { + "code": "let attributes = [\n \"device\": \"iPhone\",\n \"lifetime\": 24738388,\n \"is_logged_in\": true,\n]\n\nlet variationKey = try? optimizely.getVariationKey(experimentKey: \"my_experiment_key\",\n\t\t\t\t\t\t\t\t\t\t\t\tuserId: \"user_123\",\n attributes: attributes)\n\n", + "language": "swift" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Notes" +} +[/block] +### Activate versus Get Variation + +Use Activate when the visitor actually sees the experiment. Use Get Variation when you need to know which bucket a visitor is in before showing the visitor the experiment. Impressions are tracked by [Is Feature Enabled](doc:is-feature-enabled-csharp) when there is a feature test running on the feature and the visitor qualifies for that feature test. + +For example, suppose you want your web server to show a visitor variation_1 but don't want the visitor to count until they open a feature that isn't visible when the variation loads, like a modal. In this case, use Get Variation in the backend to specify that your web server should respond with variation_1, and use Activate in the front end when the visitor sees the experiment. + +Also, use Get Variation when you're trying to align your Optimizely results with client-side third-party analytics. In this case, use Get Variation to retrieve the variation, and even show it to the visitor, but only call Activate when the analytics call goes out. + +See [Implement impressions](doc:implement-impressions) for more information about whether to use Activate or Get Variation for a call. +[block:callout] +{ + "type": "warning", + "title": "Important", + "body": "Conversion events can only be attributed to experiments with previously tracked impressions. Impressions are tracked by Activate, not by Get Variation. As a general rule, Optimizely impressions are required for experiment results and not only for billing." +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/170 - is-feature-enabled-csharp.md b/docs/docs/sdk-reference-guides/170 - is-feature-enabled-csharp.md new file mode 100644 index 00000000..8badc3ae --- /dev/null +++ b/docs/docs/sdk-reference-guides/170 - is-feature-enabled-csharp.md @@ -0,0 +1,80 @@ +--- +title: "Is Feature Enabled" +slug: "is-feature-enabled-csharp" +hidden: false +createdAt: "2019-09-12T13:51:59.213Z" +updatedAt: "2019-09-12T20:36:35.281Z" +--- +Determines whether a feature is enabled for a given user. The purpose of this method is to allow you to separate the process of developing and deploying features from the decision to turn on a feature. Build your feature and deploy it to your application behind this flag, then turn the feature on or off for specific users. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0 +[block:api-header] +{ + "title": "Description" +} +[/block] +This method traverses the client's datafile and checks the feature flag for the feature key that you specify. +1. Analyzes the user's attributes. +2. Hashes the userID. + +The method then evaluates the feature rollout for a user. The method checks whether the rollout is enabled, whether the user qualifies for the audience targeting, and then randomly assigns either `on` or `off` based on the appropriate traffic allocation. If the feature rollout is on for a qualified user, the method returns `true`. +[block:api-header] +{ + "title": "Parameters" +} +[/block] +The table below lists the required and optional parameters in C#. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "0-0": "**featureKey**\n_required_", + "0-1": "string", + "1-0": "**userId**\n_required_", + "1-1": "string", + "2-0": "**userAttributes**\n_optional_", + "2-1": "map", + "h-2": "Description", + "0-2": "The key of the feature to check. The feature key is defined from the Features dashboard.", + "1-2": "The ID of the user to check.", + "2-2": "A map of custom key-value string pairs specifying attributes for the user that are used for audience targeting. Non-string values are only supported in the 3.0 SDK and above." + }, + "cols": 3, + "rows": 3 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +True if feature is enabled. Otherwise, false or null. +[block:api-header] +{ + "title": "Examples" +} +[/block] +This section shows a simple example of how you can use the `IsFeatureEnabled` method. +[block:code] +{ + "codes": [ + { + "code": "// Evaluate a feature flag and a variable\nbool enabled = optimizelyClient.IsFeatureEnabled(\"price_filter\", userId);\n\nint? min_price = optimizelyClient.GetFeatureVariableInteger(\"price_filter\", variableKey: \"min_price\", userId: userId);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/180 - set-forced-variation-csharp.md b/docs/docs/sdk-reference-guides/180 - set-forced-variation-csharp.md new file mode 100644 index 00000000..45cf1f7d --- /dev/null +++ b/docs/docs/sdk-reference-guides/180 - set-forced-variation-csharp.md @@ -0,0 +1,92 @@ +--- +title: "Set Forced Variation" +slug: "set-forced-variation-csharp" +hidden: false +createdAt: "2019-09-12T13:52:03.603Z" +updatedAt: "2019-09-16T23:46:57.219Z" +--- +Forces a user into a variation for a given experiment for the lifetime of the Optimizely client. + +The purpose of this method is to force a user into a specific variation or personalized experience for a given experiment. The forced variation value doesn't persist across application launches. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0, v3.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +Forces a user into a variation for a given experiment for the lifetime of the Optimizely client. Any future calls to [Activate](doc:activate-csharp), [Is Feature Enabled](doc:is-feature-enabled-csharp), [Get Feature Variable](doc:get-feature-variable-csharp), and [Track](doc:track-csharp) for the given user ID returns the forced variation. + +Forced bucketing variations take precedence over whitelisted variations, variations saved in a User Profile Service (if one exists), and the normal bucketed variation. Impression and conversion events are still tracked when forced bucketing is enabled. + +Variations are overwritten with each set method call. To clear the forced variations so that the normal bucketing flow can occur, pass null as the variation key parameter. To get the variation that has been forced, use [Get Forced Variation](doc:get-forced-variation-csharp). + +This call will fail and return false if the experiment key is not in the project file or if the variation key is not in the experiment. + +You can also use Set Forced Variation for [feature tests](doc:run-feature-tests-csharp). +[block:api-header] +{ + "title": "Parameters" +} +[/block] +This table lists the required and optional parameters for the C# SDK. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**experimentKey**\n*required*", + "0-1": "string", + "1-0": "**userId**\n*required*", + "1-1": "string", + "0-2": "The key of the experiment to set with the forced variation.", + "1-2": "The ID of the user to force into the variation.", + "2-2": "The key of the forced variation. Set the value to `null` to clear the existing experiment-to-variation mapping.", + "2-0": "**variationKey**\n*optional*", + "2-1": "string" + }, + "cols": 3, + "rows": 3 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +A Boolean value that indicates if the set completed successfully. +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "optimizelyClient.SetForcedVariation(\"my_experiment_key\", \"user_123\", \"some_variation_key\")\n \n ", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Side effects" +} +[/block] +In the receiving client instance, sets the forced variation for the specified user in the specified experiment. This forced variation is used instead of the variation that Optimizely would normally determine for that user and experiment. +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/190 - track-csharp.md b/docs/docs/sdk-reference-guides/190 - track-csharp.md new file mode 100644 index 00000000..37a6bf73 --- /dev/null +++ b/docs/docs/sdk-reference-guides/190 - track-csharp.md @@ -0,0 +1,110 @@ +--- +title: "Track" +slug: "track-csharp" +hidden: false +createdAt: "2019-09-12T13:53:40.495Z" +updatedAt: "2020-02-21T19:34:45.905Z" +--- +Tracks a conversion event. Logs an error message if the specified event key doesn't match any existing events. +[block:api-header] +{ + "title": "Version" +} +[/block] +SDK v3.0, v3.1 +[block:api-header] +{ + "title": "Description" +} +[/block] +Use this method to track events across multiple experiments. You should only send one tracking event per conversion, even if many feature tests or A/B tests are measuring it. +[block:callout] +{ + "type": "info", + "title": "Note", + "body": "Events are counted in an experiment when an impression was sent as a result of the [Activate](doc:activate-csharp) or [Is Feature Enabled](doc:is-feature-enabled-csharp) method being called." +} +[/block] +The attributes passed to Track are only used for [results segmentation](doc:analyze-results#section-segment-results). +[block:api-header] +{ + "title": "Parameters" +} +[/block] +This table lists the required and optional parameters for the C# SDK. +[block:parameters] +{ + "data": { + "h-0": "Parameter", + "h-1": "Type", + "h-2": "Description", + "0-0": "**eventKey**\n*required*", + "0-1": "string", + "1-0": "**userId**\n*required*", + "1-1": "string", + "2-0": "**userAttributes**\n*optional*", + "3-0": "**eventTags**\n*optional*", + "3-1": "map", + "2-1": "map", + "0-2": "The key of the event to be tracked. This key must match the event key provided when the event was created in the Optimizely app.", + "1-2": "The ID of the user associated with the event being tracked. \n\n**Important**: This ID must match the user ID provided to Activate or Is Feature Enabled.", + "2-2": "A map of custom key-value string pairs specifying attributes for the user that are used for [results segmentation](doc:analyze-results#section-segment-results). Non-string values are only supported in the 3.0 SDK and above.", + "3-2": "A map of key-value pairs specifying tag names and their corresponding tag values for this particular event occurrence. Values can be strings, numbers, or booleans.\n\nThese can be used to track numeric metrics, allowing you to track actions beyond conversions, for example: revenue, load time, or total value. [See details on reserved tag keys.](https://docs.developers.optimizely.com/full-stack/docs/include-event-tags#section-reserved-tag-keys)" + }, + "cols": 3, + "rows": 4 +} +[/block] + +[block:api-header] +{ + "title": "Returns" +} +[/block] +This method sends conversion data to Optimizely. It doesn't provide return values. +[block:api-header] +{ + "title": "Example" +} +[/block] + +[block:code] +{ + "codes": [ + { + "code": "using OptimizelySDK.Entity;\n\nvar attributes = new UserAttributes {\n { \"device\", \"iPhone\" },\n { \"lifetime\", 24738388 },\n { \"is_logged_in\", true },\n};\n\nvar tags = new EventTags {\n { \"category\", \"shoes\" },\n { \"count\", 2 },\n};\n\noptimizelyClient.Track(\"my_purchase_event_key\", \"user_123\", attributes, tags);\n\n", + "language": "csharp" + } + ] +} +[/block] + +[block:api-header] +{ + "title": "Side effects" +} +[/block] +The table lists other other Optimizely functionality that may be triggered by using this method. +[block:parameters] +{ + "data": { + "h-0": "Functionality", + "h-1": "Description", + "0-0": "Conversions", + "0-1": "Calling this method records a conversion and attributes it to the variations that the user has seen.\n \nFull Stack 3.x supports retroactive metrics calculation. You can create [metrics](doc:choose-metrics) on this conversion event and add metrics to experiments even after the conversion has been tracked.\n\nFor more information, see the paragraph **Events are always on** in the introduction of [Events: Tracking clicks, pageviews, and other visitor actions](https://help.optimizely.com/Measure_success%3A_Track_visitor_behaviors/Events%3A_Tracking_clicks%2C_pageviews%2C_and_other_visitor_actions).\n\n**Important!** \n - This method won't track events when the specified event key is invalid.\n - Changing the traffic allocation of running experiments affects how conversions are recorded and variations are attributed to users.", + "1-0": "Impressions", + "1-1": "Track doesn't trigger impressions.", + "2-0": "Notification Listeners", + "2-1": "Accessing this method triggers a call to the `TRACK` notification listener. \n\n**Important!** This method won't call the `TRACK` notification listener when the specified event key is invalid." + }, + "cols": 2, + "rows": 3 +} +[/block] + +[block:api-header] +{ + "title": "Source files" +} +[/block] +The language/platform source files containing the implementation for C# is [Optimizely.cs](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Optimizely.cs). \ No newline at end of file diff --git a/docs/docs/sdk-reference-guides/index.md b/docs/docs/sdk-reference-guides/index.md new file mode 100644 index 00000000..a86109cf --- /dev/null +++ b/docs/docs/sdk-reference-guides/index.md @@ -0,0 +1,14 @@ +--- +title: "C# SDK" +slug: "csharp-sdk" +hidden: false +metadata: + title: "Optimizely C# SDK" +createdAt: {} +updatedAt: "2020-05-22T20:03:21.212Z" +--- +This reference guide describes how to use the Optimizely C# SDK. Refer to the left hand navigation for all sections in this reference guide. + +The Optimizely C# SDK is currently at version 3.4.1. + +You can also use the [C# SDK quickstart](doc:c-sharp) or review the [C# SDK repo](https://github.com/optimizely/csharp-sdk) and [changelog](https://github.com/optimizely/csharp-sdk/blob/master/CHANGELOG.md). \ No newline at end of file