Giter Site home page Giter Site logo

ember-launch-darkly's Introduction

Ember Launch Darkly

Build Status

This addon wraps the Launch Darkly feature flagging service and provides helpers to implement feature flagging in your application

Compatibility

Addon version Ember version
v3.0 >= v3.28 and v4.4 README
v2.0 >= v3.17 README
<= v1.0 <= v3.16 README

Table of Contents

Installation

ember install ember-launch-darkly

Configuration

ember-launch-darkly can be configured from config/environment.js as follows:

module.exports = function (environment) {
  let ENV = {
    launchDarkly: {
      // options
    },
  };

  return ENV;
};

ember-launch-darkly supports the following configuration options:

clientSideId (required)

The client-side ID generated by Launch Darkly which is available in your account settings page. See the Launch Darkly docs for more information on how the client side ID is used.

mode

The mode in which the Launch Darkly client will run, either local or remote. When running in remote mode, feature flags will be fetched from the Launch Darkly service as you'd expect. This is the mode you want to be running in in production.

When running in local mode, feature flags will be fetched from the localFlags defined in the config. This is likely appropriate when running the app locally, or in an external environment for which you don't have Launch Darkly setup. It allows you to have a sandboxed feature flag set that is not dependent on the Launch Darkly service or the state of the flags stored in Launch Darkly itself.

Default: local

Possible Values: local, remote

localFlags

A list of initial values for your feature flags. This property is only used when mode: 'local' to populate the list of feature flags for environments such as local development where it's not desired to fetch the flags from Launch Darkly.

Default: null

streamingFlags

Streaming options for the feature flags for which you'd like to subscribe to real-time updates. See the Streaming Feature Flags section for more detailed info on what the possible options are for streaming flags.

Default: false

bootstrap

The Launch Darkly client supports the idea of bootstrapping your feature flags with an initial set of values so that the variation function can be called before the flags have been fetched from Launch Darkly.

If the bootstrap property is set to localFlags, ember-launch-darkly will use the flags specified in localFlags as the bootstrap flag values passed to Launch Darkly. Other than that, the bootstrap property will be passed directly through to Launch Darkly.

Default: null

Possible Values: localFlags otherwise whatever Launch Darkly expects based on its Bootstrapping documentation.

Launch Darkly specific config

Any other properties passed in as configuration will be passed straight through to Launch Darkly.

Possible Values: As documented in the section titled "Customizing your client" in the Launch Darkly documentation.

A note on sendEventsOnlyForVariation. When this flag is set to false, then events are sent, for every single feature flag, to Launch Darkly when client.allFlags() is called. An event is what tells Launch Darkly when a flag was last requested, which is how you can tell on the feature flags list, which flags were requested and when. This can be misleading because a user didn't actually request a flag, it was ember-launch-darkly that requested allFlags which is needed to know which flags exist. This could be confusing if a version of your code no longer has references to a feature flag but it still exists in Launch Darkly. You may see that the flag was requested even though there is no code in the wild that actually should be requesting it. Therefore, ember-launch-darkly sets this flag to true to avoid sending those events when we fetch allFlags. You are, however, welcome to set it back to false in the config if you wish - just know that this means you'll be seeing "Requested at" times for flags that you may not expect.

Usage

Initialize

Before being used, Launch Darkly must be initialized. This should happen early so choose an appropriate place to make the call such as an application initializer or the application route.

The initialize() function returns a promise that resolves when the Launch Darkly client is ready so Ember will wait until this happens before proceeding.

This function's API mirrors that of the Launch Darkly client, so see the Launch Darkly docs on initializing the client for more info.

// /app/routes/application.js

import Route from '@ember/routing/route';

import config from 'my-app/config/environment';

import { initialize } from 'ember-launch-darkly';

export default class ApplicationRoute extends Route {
  async model() {
    let user = {
      key: 'aa0ceb',
    };

    let { clientSideId, ...rest } = config;

    return await initialize(clientSideId, user, rest);
  }
}

Identify

If you initialized Launch Darkly with an anonymous user and want to re-initialize it for a specific user to receive the flags for that user, you can use identify. This must be called after initialize has been called.

// /app/routes/session.js

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

import { identify } from 'ember-launch-darkly';

export default class SessionRoute extends Route {
  @service session;

  model() {
    return this.session.getSession();
  },

  async afterModel(session) {
    let user = {
      key: session.user.id,
      firstName: session.user.firstName,
      email: session.user.email
    };

    return await identify(user);
  }
}

variation (template helper)

ember-launch-darkly provides a variation helper to check your feature flags in your handlebars templates.

If your feature flag is a boolean based flag, you might use it in an {{if}} like so:

{{#if (variation 'new-login-screen')}}
  {{login-screen}}
{{else}}
  {{old-login-screen}}
{{/if}}

If your feature flag is a multivariate based flag, you might use it in an {{with}} like so:

{{#with (variation "new-login-screen") as |variant|}}
  {{#if (eq variant "login-screen-a")}
    {{login-screen-a}}
  {{else if (eq variant "login-screen-b")}}
    {{login-screen-b}}
  {{/if}}
{{else}}
  {{login-screen}}
{{/with}}

variation (javascript helper)

If your feature flag is a boolean based flag, you might use it in a function like so:

// /app/components/login-page/component.js

import Component from '@ember/component';

import { variation } from 'ember-launch-darkly';

export default class LoginPageComponent extends Component {
  get price() {
    if (variation('new-price-plan')) {
      return 99.0;
    }

    return 199.0;
  }
}

If your feature flag is a multivariate based flag, you might use it in a function like so:

// /app/components/login-page/component.js

import Component from '@ember/component';

import { variation } from 'ember-launch-darkly';

export default class LoginPageComponent extends Component {
  get price() {
    switch (variation('new-pricing-plan')) {
      case 'plan-a':
        return 99.00;
      case 'plan-b':
        return 89.00
      case 'plan-c':
        return 79.00
      default:
        return 199.00;
    }
  }
});

Because ember-launch-darkly is built for Ember Octane, its feature flags are tracked. This means that when using the variation helper, if a flag value changes, code that references it will be automatically recomputed.

Local feature flags

When mode: 'local' is set in the Launch Darkly configuration, ember-launch-darkly will retrieve the feature flags and their values from config/environment.js instead of the Launch Darkly service. This is useful for development purposes so you don't need to set up a new environment in Launch Darkly, your app doesn't need to make a request for the flags, and you can easily change the value of the flags from the browser console.

The local feature flags are defined in config/environment.js like so:

let ENV = {
  launchDarkly: {
    mode: 'local',
    localFlags: {
      'apply-discount': true,
      'new-pricing-plan': 'plan-a',
    },
  },
};

When mode: 'local', the Launch Darkly flags context is available in the JS console via window.__LD__. The context object provides the following helper methods to manipulate feature flags:

> window.__LD__.get('new-pricing-plan', 'plan-a') // return the current value of the feature flag providing a default ('plan-a' if it doesn't exist (the default is optional)

> window.__LD__.set('new-pricing-plan', 'plan-x') // set the variation value

> window.__LD__.enable('apply-discount') // helper to set the return value to `true`
> window.__LD__.disable('apply-discount') // helper to set the return value to `false`

> window.__LD__.allFlags // return the current list of feature flags and their values

> window.__LD__.user // return the user that the client has been initialized with

Persisting local feature flags

When mode: 'local' there is also an option to 'persist' the flags to localStorage. This could be useful if you don't want to enable a flag yet for other users, but need it to be enabled for your own scenario.

//setting the flag as usual
> window.__LD__.set('stringFlag', 'goodbye')

// this persists all the set flags to localStorage, so when the page is refreshed they are loaded from there
> window.__LD__.persist()

//to unset the flags you can use
> window.__LD__.resetPersistence()

Streaming feature flags

Launch Darkly supports the ability to subscribe to changes to feature flags so that apps can react in real-time to these changes. The streamingFlags configuration option allows you to specify, in a couple of ways, which flags you'd like to stream.

To disable streaming completely, use the following configuration:

launchDarkly: {
  streamingFlags: false;
}

Note, this is the default behaviour if the streamingFlags option is not specified.

To stream all flags, use the following configuration:

launchDarkly: {
  streamingFlags: true;
}

To get more specific, you can select to stream all flags except those specified:

launchDarkly: {
  streamingFlags: {
    allExcept: ['apply-discount', 'new-login'];
  }
}

And, finally, you can specify only which flags you would like to stream:

launchDarkly: {
  streamingFlags: {
    'apply-discount': true
  }
}

As Launch Darkly's real-time updates to flags uses the Event Source API, certain browsers will require a polyfill to be included. ember-launch-darkly uses EmberCLI targets to automatically decide whether or not to include the polyfill. Ensure your project contains a valid config/targets.js file if you require this functionality.

Content Security Policy

If you have CSP enabled in your ember application, you will need to add Launch Darkly to the connect-src like so:

// config/environment.js

module.exports = function (environment) {
  let ENV = {
    //snip

    contentSecurityPolicy: {
      'connect-src': ['https://*.launchdarkly.com'],
    },

    //snip
  };
};

Test helpers

Acceptance tests

Add the setupLaunchDarkly hook to the top of your test file. This will ensure that Launch Darkly uses defaults your feature flags to false instead of using what is defined in the localFlags config. This allows your tests to start off in a known default state.

import { module, test } from 'qunit';
import { visit, currentURL, click } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';

import { setupLaunchDarkly } from 'ember-launch-darkly/test-support';

module('Acceptance | Homepage', function (hooks) {
  setupApplicationTest(hooks);
  setupLaunchDarkly(hooks);

  test('links go to the new homepage', async function (assert) {
    await visit('/');
    await click('a.pricing');

    assert.equal(
      currentRoute(),
      'pricing',
      'Should be on the old pricing page'
    );
  });
});

ember-launch-darkly provides a test helper, withVariation, to make it easy to turn feature flags on and off in acceptance tests.

module('Acceptance | Homepage', function (hooks) {
  setupApplicationTest(hooks);
  setupLaunchDarkly(hooks);

  test('links go to the new homepage', async function (assert) {
    await this.withVariation('new-pricing-plan', 'plan-a');

    await visit('/');
    await click('a.pricing');

    assert.equal(
      currentRoute(),
      'pricing',
      'Should be on the old pricing page'
    );
  });
});

Integration tests

Use the setupLaunchDarkly hook and withVariation helper in component tests to control feature flags as well.

import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

import { setupLaunchDarkly } from 'ember-launch-darkly/test-support';

module('Integration | Component | foo', function (hooks) {
  setupRenderingTest(hooks);
  setupLaunchDarkly(hooks);

  test('new pricing', async function (assert) {
    await render(hbs`
      {{#if (variation "new-pricing-page")}}
        <h1 class="price">ยฃ 99</h1>
      {{else}}
        <h1 class="price">ยฃ 199</h1>
      {{/if}}
    `);

    await this.withVariation('new-pricing-page');

    assert.equal(
      this.element.querySelector('.price').textContent.trim(),
      'ยฃ 99',
      'New pricing displayed'
    );
  });
});

Upgrading to v2.0

v2.0 of the addon is built for Ember Octane (>= v3.17) and beyond. It contains breaking changes from the previous releases. If you would like to upgrade from v1.0 or earlier, please following the instructions in UPGRADING_TO_v2.x.md file.

Made with โค๏ธ by The Ember Launch Darkly Team

Upgrading to v3.0

Below you can find a list of deprecations, most of them should already be working correctly with modern ember apps:

  • Dropped node support below v16
  • Dropped ember support below LTS v3.28 and 4.4

Deprecations

  • The Context exposed an undocumented getter user which returned the value of Launchdarkly's getUser() helper. With Launchdarkly's v3 release, getUser() has been removed in favor of getContext(). Both should return the same, so for now we replaced it to just return getContext(). We still recommend using the native helpers as much as possible since the client is exposed through the addon. This avoids breaking changes in the addon in the future. We will remove the user getter in the next major release.

ember-launch-darkly's People

Contributors

achambers avatar aklkv avatar alexis-falaise avatar bors[bot] avatar chrisvdp avatar dependabot-preview[bot] avatar dependabot[bot] avatar ember-tomster avatar herzzanu avatar jaredgalanis avatar knownasilya avatar mansona avatar patcoll avatar rlivsey avatar robbiethewagner avatar rstudner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-launch-darkly's Issues

Possible memory leak

Hello there! Thanks for this great addon ๐Ÿ˜„

We think we've identified a memory leak coming from this addon. We ran a unit test that didn't even boot the Ember app and noticed a dangling reference to container. When we uninstalled this addon, the reference went away.

So that tells me it might be something either in index.js, or in one of the initializers (if they even get run in the unit test).

Just wanted to open this to ask if this is known, and/or if we start diving in to fix it, is someone watching the repo who will be able to get this fix merged.

Thanks!

Computed Property is not Firing

So I started to play with this addon and when the page loads, I can see my flags and its working. I am trying to setup a computed property so when the flag changes, the computed property fires. Now I could be missing something.

When the page first loads I see the network call to LD and I see my flag. I click on a different route and then I click to come back. I see in the network tab that it calls LD again and I see the flag come back the value has changed. But my computed property does not re-fire.

/route/application.js

model() {
  let user = {
    key: 'MyApp',
    custom: {
      LocationID: +locationId,
    },
  };

  return this.launchDarkly.initialize(user),
} 

/controller/mypage.js

@computed('launchDarkly.sell-memberships')
get isSellMembership() {
  debugger;

  return this.launchDarkly.variation('sell-memberships');
}

/template/mypage.hbs

{{#if this.isSellMembership}}
  <h1>True</h1>
{{else}}
  <h1>False</h1>
{{/if}}

I am sure I am missing something, but not sure what.

Update 1: I have added a debugger to the computed property and it never refires.

Prepare for v3

TODO


  • Node | support v16 and v18 as LTS from their release cycles. Remove support for older versions.
  • Ember | only support LTS 3.28 and 4.4 from their release cycles. Remove support for older versions.
  • Support ember-auto-import v2. Remove ember-auto-import v1 support #353
  • Be embroider compatible #328
  • Upgrade tracked-maps-and-sets(dependant on the Node and Ember upgrades) #315 (comment)
  • Replace getUser() with getContext(): #409 (comment)
  • Update readme with correct supported ember and node versions

Concern about the future of this addon

Hey folks! I'm glad someone grabbed this repo and was able to secure apparently everything about it, including past issues, etc. I don't have any relationship with Kayako or any of its employees but the disappearance of its repos is concerning.

I lead a team that's just started to depend on this addon, and I wonder -- is there anything that can be done to help sustain it for the future? Does it need a new sponsor or a new home?

Improve the release process

We're using release-it for releasing new versions and lerna changelog through the lerna changelog release-it plugin. For some reason this is currently broken. We need to fix it and possible improve the process. Esentially what should happen when releasing:

  • update tag and version
  • update changelog
  • publish new version to npm
  • publish new release to github

An alternative to the release-it package is https://github.com/changesets/changesets We can experiment with this package as well and see if it does a better job at releasing "stress free".

The plugin function launchDarklyVariationHelperPlugin didn't export a Plugin instance

I haven't looked into this very deeply, but I wanted to put this here as a placeholder for conversation.

I added this addon to our Ember app (Node 6.11.1, Ember 2.11.0) with the right configuration, and when building the app I get the following error and the entire build fails. It even fails without the config on the ENV.

I've tried "nombom" and still with the same result. I checked out the ember-launch-darkly repo and added an ember-try case for 2.11.0 and all tests pass. So it might have to do with the unique setup we have.

Has anyone seen any error like this before? From a quick Google search, I found some similar errors and saw comments about a discrepancy between Babel 5 and 6. Our app uses ember-cli 2.11.0, which doesn't use ember-cli-babel 6.x, so I wonder if that's related somehow?

Any thoughts would be greatly appreciated! I'll be digging into this more.

Build failed.
File: ember-ajax/-private/promise.js
The Broccoli Plugin: [broccoli-persistent-filter:Babel] failed with:
TypeError: The plugin function launchDarklyVariationHelperPlugin({ types: t }) {
  return {
    name: 'launch-darkly-variation-helper',
    visitor: {
      Program: {
        enter(path, state) {
          let variationImport = _findVariationHelperImport(path, t);

          if (variationImport && _isReferenced(variationImport, t)) {
            state.variationHelperReferenced = true;
          }
        },

        exit(path, state) {
          let variationImport = _findVariationHelperImport(path, t);

          if (variationImport) {
            _removeSpecifierOrImport(variationImport, t);

            if (state.variationHelperReferenced) {
              _insertServiceImport(path, t);
            }
          }
        }
      },

      Identifier(path, state) {
        if (path.referencesImport(MODULE_NAME, MEMBER_NAME)) {
          let parentCallExpression = path.findParent(p => t.isCallExpression(p));
          let key = parentCallExpression.get('arguments.0').node.value;
          parentCallExpression.replaceWith(_build(key, t));

          let { parent, type } = _findParent(parentCallExpression, t);

          switch (type) {
            case 'computed-property': {
              let dependentKey = `${SERVICE_PROPERTY_NAME}.${key}`;

              if (_shouldInjectDependentKey(key, parent, t)) {
                parent.node.arguments.unshift(t.stringLiteral(dependentKey));
              }

              let fn = parent.get('arguments').find(a => t.isFunctionExpression(a));

              if (fn && !_containsServiceDeclaration(fn, t)) {
                _insertServiceDeclaration(fn, t);
              }

              return;
            }
            case 'function': {
              _insertServiceDeclaration(parent, t);
              return;
            }
          }
        }
      },

      CallExpression(path, state) {
        if (state.variationHelperReferenced) {
          _insertServiceInjection(path, t);
        }
      }
    }
  };
} didn't export a Plugin instance
    at PluginManager.validate (<app>/node_modules/babel-core/lib/transformation/file/plugin-manager.js:164:13)
    at PluginManager.add (<app>/node_modules/babel-core/lib/transformation/file/plugin-manager.js:213:10)
    at File.buildTransformers (<app>/node_modules/babel-core/lib/transformation/file/index.js:237:21)
    at new File (<app>/node_modules/babel-core/lib/transformation/file/index.js:139:10)
    at Pipeline.transform (<app>/node_modules/babel-core/lib/transformation/pipeline.js:164:16)
    at Babel.transform (<app>/node_modules/broccoli-babel-transpiler/index.js:107:21)
    at Babel.processString (<app>/node_modules/broccoli-babel-transpiler/index.js:206:25)
    at Promise.then.result.output (<app>/node_modules/broccoli-persistent-filter/lib/strategies/persistent.js:41:23)
    at initializePromise (<app>/node_modules/rsvp/dist/rsvp.js:588:5)
    at new Promise (<app>/node_modules/rsvp/dist/rsvp.js:1076:31)

The broccoli plugin was instantiated at:
    at Babel.Plugin (<app>/node_modules/broccoli-plugin/index.js:7:31)
    at Babel.Filter [as constructor] (<app>/node_modules/broccoli-persistent-filter/index.js:62:10)
    at new Babel (<app>/node_modules/broccoli-babel-transpiler/index.js:35:10)
    at EmberApp._addonTree (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:940:29)
    at EmberApp._processedVendorTree (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:974:20)
    at EmberApp._processedExternalTree (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:1005:21)
    at EmberApp.appAndDependencies (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:1114:30)
    at EmberApp.javascript (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:1244:34)
    at EmberApp.toArray (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:1674:10)
    at EmberApp.toTree (<app>/node_modules/ember-cli/lib/broccoli/ember-app.js:1696:30)

_client can be null causing type error

We are calling this.launchDarkly.variation('feature-flag') within our code (the application controller), but based on network timing the client onReady event may not have fired yet. This means that the current value of _client on
https://github.com/ember-launch-darkly/ember-launch-darkly/blob/df0bd2567d896702cde8b6799902b0e4ba177324/addon/services/launch-darkly-client-remote.js#L61
is null, causing a TypeError:

launch-darkly-client-remote.js:62 Uncaught TypeError: Cannot read property 'variation' of null
    at Class.variation (launch-darkly-client-remote.js:62)
    at Proxy.variation (launch-darkly.js:36)
    at Class.<anonymous> (application.js:77)

Ideally, the service would defend against _service as null, possibly by init'ing it to nullClient

`allFlags` triggers `variation` call which registers "last requested" time in LD dashboard

Our LD dashboard seemed to always show the "last requested" timestamp for every feature as being a second ago, even for features that were not turned on.

As well as that, it seemed that each feature was being requested as soon as a used opened the app.

This meant that we couldn't see the true state of a feature flag and whether it truly was removable and not being used anymore.

I finally nailed it down to this line: addon/services/launch-darkly-remote.js#L66 which. It turns out that the allFlags function calls through to the variation function under the covers which in turn sends the event request that updates the "Last requested" timestamp.

We need to think of an alternate approach.

๐Ÿ‘‘ [QUEST] Get repo up to date and release v1.0

This is a quest issue to track the things we want/need to do to get this addon back on the happy upgrade path and get a v1.0 released:

  • Resolve Dependabot PRs
  • Put a test suite in place that gives us confidence
  • Update to the latest LD JS client lib
  • Get tests running on Travis (with ember-try)
  • Add Dependabot
  • Update Ember/EmberCLI version (currently to 3.11)
  • Fix the babel transform story so that it is deterministic, reliable, and allows us to release with confidence

Support for LaunchDarkly's contexts upgrade

LaunchDarkly plans changes in a way they track monthly usage. They will start the changes from the 1st of April and switch from MAU (Monthly Active Users) to MCI (Monthly Context Instances). You can read more about it here: https://docs.launchdarkly.com/guides/flags/upgrading-contexts.

To use contexts we would have to bump launchdarkly-js-client-sdk to version 3.0.0. I'm not yet sure what would be involved in the addon to start supporting it but just wanted to flag it

Remove the use of observers

In #130 I needed to make an explicit eslint ignore comment to allow us to use observers without crashing CI.

I wanted to create this issue to keep track of any efforts to remove the use of observers ๐Ÿ‘

Update ember/ember-cli version

This addon is super out of date and it feels like upgrading the ember/ember-cli version might be a good starting point in updating things like the LD js client lib and fixing a few other anomalies.

Of the top of my head, a few things that are affected when updating the ember version are:

  • The way the instance-initializer exposes the ld client on window
  • The way withVariation helper should be used
  • The way the test client should be used
  • The ember-cli-shims imports need to be updated
  • The babel transform needs to be updated to support the new imports

Release 2.1.0

I would like to release v2.1.0 including which would include the latest SDK version: #327

I don't believe there are any other urgent dependencies that need to be updated.

Prepare for v2

TODO

  • Implement for an Octane/@tracked world (@achambers #235)
  • Codemods for upgrading?
  • Move to Github Actions CI?
  • Better use of dummy app to smoke test changes to repo
  • Find a suitable acceptance testing approach.

Releated issues

  • Closes #9 because we can now pass through any SDK options to the underlying LD SDK client initialize function
  • Closes #21 because we no longer include the Babel transform
  • Closes #60 because we now do this
  • Closes #131 because we now use tracked properties

Export getCurrentContext function from -sdk module

The getCurrentContext function may be useful to users (instead of accessing explicitly window.__LD__) to access the user context.

A use case I've in mind is when you want to enrich the data you passed previously to identify() by doing another call to it. In this case, it's handy to get the current user context, instead of gathering again all the needed data.

Right now, you can import it by doing

import { getCurrentContext } from 'ember-launch-darkly/-sdk/context';

I think it would be nice to export it from the -sdk module index, so that's clear that the function is intended to be used from the outside, as identify, variation, etc.

What does this addon look like in an `@tracked`/Octane world?

A placeholder issue to start a discussion around this topic. I'll do some exploration myself and build some context around it here soon. But anyone else interested in diving in is welcome to make a start on it too.

Brain dump

These functions are going to be related to this:

_registerComputedProperty(key) {
let self = this;
defineProperty(this, key, computed(function() {
return self.variation(key);
}));
},
_registerSubscription(key) {
this.get('_client').on(key, () => {
this.notifyPropertyChange(key);
})
},
_notifyFlagUpdates() {
this._seenFlags.forEach(key => this.notifyPropertyChange(key));
return RSVP.resolve();
},
unknownProperty(key) {
if (this._seenFlags.indexOf(key) === -1) {
this._seenFlags.push(key);
}
this._registerComputedProperty(key);
this._registerSubscription(key);
return this.variation(key);
}

This might come in to play too:

service.addObserver(key, this, 'recompute');

As is this Babel plugin (potentially, and hopefully, not needed in an @tracked world):

https://github.com/adopted-ember-addons/ember-launch-darkly/blob/master/babel-plugin/index.js

Anonymous users

How are you currently handling anonymous users in your use of the addon?

My initial thoughts are to do something like the following:

  • In my application route's beforeModel
    • Generate a UUID for every visitor
    • Store the UUID in the cookie store
    • Attempt to load my current user (using ember-simple-auth)
  • In my application route's model
    • Initialize LaunchDarkly
      • with the user, if available
      • with the anonymous user, using the UUID from the cookie store (and also setting the anonymous key)

There's no way to ensure that the anonymous users will be the same users on repeat visits, but it makes it much more likely that they won't be served different UIs (if we even used feature flags for anonymous users).

I'm curious if you do something similar.

I know it's a bit outside the scope of this addon per se, but it would be nice to have some guides about some best practices here. Would be happy to put something together if we agree what is actually a best practice! ๐Ÿ˜‚

Thanks in advance!

Update launchdarkly-js-client-sdk to newer version

We're happily using this addon, but we need some newer features from the underlying LD SDK, namey:

  • auto aliasing from 2.19.0
  • LDOptions.requestHeaderTransform from 2.20

Would it be feasible to upgrade? Is it supported to install the SDK in our own package.json deps?

Implement a persisted mode for development

The use of the window.__LD__ context to get and set flags is quite straightforward and comes in handy.
However in some use cases involving a reload of the page, we may lose the context and have to set it again manually.

This is a proposal / discussion opener to implement a persisted mode for development purposes, based on the browser's localStorage or similar alternatives.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.