-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(flags): add launch darkly integration #14130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
26722d7
Init package files
aliu39 72f9cc0
Merge branch 'develop' into aliu/launch-darkly
aliu39 419cd83
Revert changelog and move types file
aliu39 610da4d
Add ld to dependencies and skeleton code. Get rid of core/
aliu39 5b2e5ec
Fix readme, rename types file, bring back core/
aliu39 66b1253
Fix readme 2
aliu39 023604a
Merge branch 'develop' into aliu/launch-darkly
aliu39 1f99c28
Merge branch 'aliu/launch-darkly' of https://github.com/getsentry/sen…
aliu39 85acc6d
Finish implementing, minus scope.flags
aliu39 3b3a767
Implement flag buffer in sentry scope
aliu39 ab3181d
Revert changelog
aliu39 22684ea
fix types
michellewzhang 79e5b24
docstring
michellewzhang 63649c5
Export FeatureFlag type in index.ts
aliu39 923500f
Merge branch 'aliu/launch-darkly' of https://github.com/getsentry/sen…
aliu39 bd04755
Clean up comments
aliu39 91a4db9
Fix build (uses yalc for scope changes) and use LRUMap util
aliu39 1849673
Merge branch 'develop' into aliu/launch-darkly
aliu39 1ca9d53
Tweak hook name and docs
aliu39 893cd62
FlagBuffer class and interface, add to scope._contexts. Remove old fl…
aliu39 f172683
Call clone in scope.clone()
aliu39 b96c17f
Rewrite as a util fx instead of class. Use in LD integration
aliu39 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
build/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file | ||
// lives | ||
|
||
// ESLint config docs: https://eslint.org/docs/user-guide/configuring/ | ||
|
||
module.exports = { | ||
extends: ['../../.eslintrc.js'], | ||
overrides: [ | ||
{ | ||
files: ['src/**/*.ts'], | ||
}, | ||
{ | ||
files: ['test.setup.ts', 'vitest.config.ts'], | ||
parserOptions: { | ||
project: ['tsconfig.test.json'], | ||
}, | ||
rules: { | ||
'no-console': 'off', | ||
}, | ||
}, | ||
{ | ||
files: ['test/**/*.ts'], | ||
|
||
rules: { | ||
// most of these errors come from `new Promise(process.nextTick)` | ||
'@typescript-eslint/unbound-method': 'off', | ||
// TODO: decide if we want to enable this again after the migration | ||
// We can take the freedom to be a bit more lenient with tests | ||
'@typescript-eslint/no-floating-promises': 'off', | ||
}, | ||
}, | ||
{ | ||
files: ['src/types/deprecated.ts'], | ||
rules: { | ||
'@typescript-eslint/naming-convention': 'off', | ||
}, | ||
}, | ||
], | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
/*.tgz | ||
.eslintcache | ||
build |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023-2024 Functional Software, Inc. dba Sentry | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<p align="center"> | ||
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank"> | ||
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84"> | ||
</a> | ||
</p> | ||
|
||
# Sentry Integration for LaunchDarkly | ||
|
||
This SDK is **considered experimental and in a beta state**. It may experience breaking changes, and may be discontinued | ||
at any time. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have | ||
any feedback/concerns. | ||
|
||
## Installation | ||
|
||
## Configuration |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
{ | ||
"name": "@sentry/launchdarkly", | ||
"version": "8.35.0", | ||
"description": "Sentry SDK integration for Launch Darkly feature flagging", | ||
"repository": "git://github.com/getsentry/sentry-javascript.git", | ||
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/launchdarkly", | ||
"author": "Sentry", | ||
"license": "MIT", | ||
"engines": { | ||
"node": ">=14.18" | ||
}, | ||
"files": [ | ||
"/build/npm" | ||
], | ||
"main": "build/npm/cjs/index.js", | ||
"module": "build/npm/esm/index.js", | ||
"types": "build/npm/types/index.d.ts", | ||
"exports": { | ||
"./package.json": "./package.json", | ||
".": { | ||
"import": { | ||
"types": "./build/npm/types/index.d.ts", | ||
"default": "./build/npm/esm/index.js" | ||
}, | ||
"require": { | ||
"types": "./build/npm/types/index.d.ts", | ||
"default": "./build/npm/cjs/index.js" | ||
} | ||
} | ||
}, | ||
"typesVersions": { | ||
"<4.9": { | ||
"build/npm/types/index.d.ts": [ | ||
"build/npm/types-ts3.8/index.d.ts" | ||
] | ||
} | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@sentry/browser": "^8.35.0", | ||
"@sentry/core": "8.35.0", | ||
"@sentry/types": "8.35.0", | ||
"@sentry/utils": "8.35.0", | ||
"launchdarkly-js-client-sdk": "^3.5.0" | ||
}, | ||
"scripts": { | ||
"build": "run-p build:transpile build:types build:bundle", | ||
"build:transpile": "rollup -c rollup.npm.config.mjs", | ||
"build:bundle": "rollup -c rollup.bundle.config.mjs", | ||
"build:dev": "run-p build:transpile build:types", | ||
"build:types": "run-s build:types:core build:types:downlevel", | ||
"build:types:core": "tsc -p tsconfig.types.json", | ||
"build:types:downlevel": "yarn downlevel-dts build/npm/types build/npm/types-ts3.8 --to ts3.8 && yarn node ./scripts/shim-preact-export.js", | ||
"build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch", | ||
"build:dev:watch": "run-p build:transpile:watch build:types:watch", | ||
"build:transpile:watch": "yarn build:transpile --watch", | ||
"build:bundle:watch": "yarn build:bundle --watch", | ||
"build:types:watch": "tsc -p tsconfig.types.json --watch", | ||
"build:tarball": "npm pack", | ||
"circularDepCheck": "madge --circular src/index.ts", | ||
"clean": "rimraf build sentry-internal-launchdarkly-*.tgz", | ||
"fix": "eslint . --format stylish --fix", | ||
"lint": "eslint . --format stylish", | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"yalc:publish": "yalc publish --push --sig" | ||
}, | ||
"volta": { | ||
"extends": "../../package.json" | ||
}, | ||
"sideEffects": false | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils'; | ||
|
||
const baseBundleConfig = makeBaseBundleConfig({ | ||
bundleType: 'addon', | ||
entrypoints: ['src/index.ts'], | ||
licenseTitle: '@sentry/launchdarkly', | ||
outputFileBase: () => 'bundles/launchdarkly', | ||
}); | ||
|
||
const builds = makeBundleConfigVariants(baseBundleConfig); | ||
|
||
export default builds; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils'; | ||
|
||
export default makeNPMConfigVariants( | ||
makeBaseNPMConfig({ | ||
hasBundles: true, | ||
packageSpecificConfig: { | ||
output: { | ||
// set exports to 'named' or 'auto' so that rollup doesn't warn | ||
exports: 'named', | ||
// set preserveModules to false because for Replay we actually want | ||
// to bundle everything into one file. | ||
preserveModules: | ||
process.env.SENTRY_BUILD_PRESERVE_MODULES === undefined | ||
? false | ||
: Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES), | ||
}, | ||
}, | ||
}), | ||
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// preact does not support more modern TypeScript versions, which breaks our users that depend on older | ||
// TypeScript versions. To fix this, we shim the types from preact to be any and remove the dependency on preact | ||
// for types directly. This script is meant to be run after the build/npm/types-ts3.8 directory is created. | ||
|
||
// Path: build/npm/types-ts3.8/global.d.ts | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
/** | ||
* This regex looks for preact imports we can replace and shim out. | ||
* | ||
* Example: | ||
* import { ComponentChildren, VNode } from 'preact'; | ||
*/ | ||
const preactImportRegex = /import\s*{\s*([\w\s,]+)\s*}\s*from\s*'preact'\s*;?/; | ||
|
||
function walk(dir) { | ||
const files = fs.readdirSync(dir); | ||
files.forEach(file => { | ||
const filePath = path.join(dir, file); | ||
const stat = fs.lstatSync(filePath); | ||
if (stat.isDirectory()) { | ||
walk(filePath); | ||
} else { | ||
if (filePath.endsWith('.d.ts')) { | ||
const content = fs.readFileSync(filePath, 'utf8'); | ||
Check failureCode scanning / CodeQL Potential file system race condition High
The file may have changed since it
was checked Error loading related location Loading |
||
const capture = preactImportRegex.exec(content); | ||
if (capture) { | ||
const groups = capture[1].split(',').map(s => s.trim()); | ||
|
||
// This generates a shim snippet to replace the type imports from preact | ||
// It generates a snippet based on the capture groups of preactImportRegex. | ||
// | ||
// Example: | ||
// | ||
// import type { ComponentChildren, VNode } from 'preact'; | ||
// becomes | ||
// type ComponentChildren: any; | ||
// type VNode: any; | ||
const snippet = groups.reduce((acc, curr) => { | ||
const searchableValue = curr.includes(' as ') ? curr.split(' as ')[1] : curr; | ||
|
||
// look to see if imported as value, then we have to use declare const | ||
if (content.includes(`typeof ${searchableValue}`)) { | ||
return `${acc}declare const ${searchableValue}: any;\n`; | ||
} | ||
|
||
// look to see if generic type like Foo<T> | ||
if (content.includes(`${searchableValue}<`)) { | ||
return `${acc}type ${searchableValue}<T> = any;\n`; | ||
} | ||
|
||
// otherwise we can just leave as type | ||
return `${acc}type ${searchableValue} = any;\n`; | ||
}, ''); | ||
|
||
// we then can remove the import from preact | ||
const newContent = content.replace(preactImportRegex, '// replaced import from preact'); | ||
|
||
// and write the new content to the file | ||
fs.writeFileSync(filePath, snippet + newContent, 'utf8'); | ||
Check failureCode scanning / CodeQL Potential file system race condition High
The file may have changed since it
was checked Error loading related location Loading |
||
} | ||
} | ||
} | ||
}); | ||
} | ||
|
||
function run() { | ||
// recurse through build/npm/types-ts3.8 directory | ||
const dir = path.join('build', 'npm', 'types-ts3.8'); | ||
walk(dir); | ||
} | ||
|
||
run(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* eslint-disable @sentry-internal/sdk/no-class-field-initializers */ | ||
|
||
import * as Sentry from '@sentry/browser'; | ||
import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@sentry/types'; | ||
import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk'; | ||
import type { LaunchDarklyOptions } from '../types'; | ||
import { insertToFlagBuffer } from '@sentry/utils'; | ||
|
||
/** | ||
* Sentry integration for capturing feature flags from LaunchDarkly. | ||
* | ||
* See the [feature flag documentation](TODO:) for more information. | ||
* | ||
* @example | ||
* ``` | ||
* import {SentryInspector, launchDarklyIntegration} from '@sentry/launchdarkly'; | ||
* import {LDClient} from 'launchdarkly-js-client-sdk'; | ||
* | ||
* Sentry.init(..., integrations: [launchDarklyIntegration()]) | ||
* const ldClient = LDClient.initialize(..., inspectors: [SentryInspector]); | ||
* ``` | ||
*/ | ||
export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => { | ||
return { | ||
name: 'launchdarkly', | ||
|
||
processEvent(event: Event, _hint: EventHint, _client: SentryClient): Event { | ||
const scope = Sentry.getCurrentScope(); | ||
const flagContext = scope.getScopeData().contexts.flags; | ||
|
||
if (event.contexts === undefined) { | ||
event.contexts = {}; | ||
} | ||
event.contexts.flags = flagContext; | ||
return event; | ||
}, | ||
}; | ||
}) satisfies IntegrationFn; | ||
|
||
/** | ||
* LaunchDarkly hook that listens for flag evaluations and updates the | ||
* flagBuffer in our current scope. | ||
* | ||
* This needs to be registered separately in the LDClient, after initializing | ||
* Sentry. | ||
*/ | ||
export class SentryInspector implements LDInspectionFlagUsedHandler { | ||
public name = 'sentry-flag-auditor'; | ||
|
||
public type = 'flag-used' as const; | ||
|
||
// We don't want the handler to impact the performance of the user's flag evaluations. | ||
public synchronous = false; | ||
|
||
/** | ||
* Handle a flag evaluation by storing its name and value on the current scope. | ||
*/ | ||
public method(flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext): void { | ||
if (typeof flagDetail.value === 'boolean') { | ||
const scopeContexts = Sentry.getCurrentScope().getScopeData().contexts; | ||
if (!scopeContexts.flags) { | ||
scopeContexts.flags = {values: []} | ||
} | ||
const flagBuffer = scopeContexts.flags.values; | ||
insertToFlagBuffer(flagBuffer, flagKey, flagDetail.value); | ||
} | ||
return; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// This file is used as entry point to generate the npm package and CDN bundles. | ||
|
||
export { launchDarklyIntegration } from './core/integration'; | ||
|
||
// export type { | ||
// } from './types'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type LaunchDarklyOptions = Record<string, never>; //TODO: |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.