Skip to content

Commit 65c2371

Browse files
authored
feat: plugins can now disable integrations by marking them as false in the event.integrations (#496)
1 parent 4de8c9e commit 65c2371

File tree

10 files changed

+111
-36
lines changed

10 files changed

+111
-36
lines changed

example/e2e/mockServer.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,23 @@ export const startServer = async (mockServerListener) => {
1515

1616
app.use(bodyParser.json());
1717

18-
app.post('/', (req, res) => {
18+
// Handles batch events
19+
app.post('/events', (req, res) => {
1920
console.log(`➡️ Received request`);
2021
mockServerListener(req.body);
2122
res.status(200).send({ mockSuccess: true });
2223
});
2324

25+
// Handles settings calls
26+
app.get('/settings/:writeKey/*', (req, res) => {
27+
console.log(`➡️ Replying with Settings`);
28+
res.status(200).send({
29+
integrations: {
30+
'Segment.io': {},
31+
},
32+
});
33+
});
34+
2435
server = app.listen(port, () => {
2536
console.log(`🚀 Started mock server on port ${port}`);
2637
resolve();

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"babel-jest": "^26.6.3",
4343
"babel-plugin-module-resolver": "^4.1.0",
4444
"body-parser": "^1.19.0",
45-
"detox": "^18.23.1",
45+
"detox": "^19.6.0",
4646
"eslint": "8.2.0",
4747
"express": "^4.17.1",
4848
"jest": "^27.3.1",

example/yarn.lock

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,16 @@ ajv@^6.10.0, ajv@^6.12.4:
19021902
json-schema-traverse "^0.4.1"
19031903
uri-js "^4.2.2"
19041904

1905+
ajv@^8.6.3:
1906+
version "8.11.0"
1907+
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
1908+
integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
1909+
dependencies:
1910+
fast-deep-equal "^3.1.1"
1911+
json-schema-traverse "^1.0.0"
1912+
require-from-string "^2.0.2"
1913+
uri-js "^4.2.2"
1914+
19051915
anser@^1.4.9:
19061916
version "1.4.10"
19071917
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b"
@@ -2436,10 +2446,10 @@ buffer@^5.2.0:
24362446
base64-js "^1.3.1"
24372447
ieee754 "^1.1.13"
24382448

2439-
bunyan-debug-stream@^1.1.0:
2440-
version "1.1.2"
2441-
resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-1.1.2.tgz#3d09a788a8ddf37a23b6840e7e19cf46239bc7b4"
2442-
integrity sha512-mNU4QelBu9tUyE6VA0+AQdyillEMefx/2h7xkNL1Uvhw5w9JWtwGWAb7Rdnmj9opmwEaPrRvnJSy2+c1q47+sA==
2449+
bunyan-debug-stream@^2.0.1:
2450+
version "2.0.1"
2451+
resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-2.0.1.tgz#9bd7c7e30c7b2cf711317e9d37529b0464c3b164"
2452+
integrity sha512-MCEoqggU7NMt7f2O+PU8VkqfSkoQoa4lmN/OWhaRfqFRBF1Se2TOXQyLF6NxC+EtfrdthnquQe8jOe83fpEoGA==
24432453
dependencies:
24442454
colors "1.4.0"
24452455
exception-formatter "^1.0.4"
@@ -3004,19 +3014,19 @@ detect-newline@^3.0.0:
30043014
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
30053015
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
30063016

3007-
detox@^18.23.1:
3008-
version "18.23.1"
3009-
resolved "https://registry.yarnpkg.com/detox/-/detox-18.23.1.tgz#f09f5e50291cdab3d62dc40ff2e8bb5cfb3cb776"
3010-
integrity sha512-MnOXfTcBBcXTrlLk3EeHq1nEfob79nChZbfOtlEummyec/X+PQzEvmKk2cvsUzu1f7GiNbCiBKN66w47Z7b/CQ==
3017+
detox@^19.6.0:
3018+
version "19.6.0"
3019+
resolved "https://registry.yarnpkg.com/detox/-/detox-19.6.0.tgz#eaef86524dc6184b5bf2e841a5f00968f5fe4813"
3020+
integrity sha512-TEoi19rJQIValWrvHf6ensOxw1smykj3qemvqOGF+KJT5pf5WcPgEpNI/Z6/9AipGqEhgbTDt7GpOnA7WS+VNQ==
30113021
dependencies:
3022+
ajv "^8.6.3"
30123023
bunyan "^1.8.12"
3013-
bunyan-debug-stream "^1.1.0"
3024+
bunyan-debug-stream "^2.0.1"
30143025
chalk "^2.4.2"
30153026
child-process-promise "^2.2.0"
30163027
find-up "^4.1.0"
30173028
fs-extra "^4.0.2"
30183029
funpermaproxy "^1.0.1"
3019-
get-port "^2.1.0"
30203030
ini "^1.3.4"
30213031
lodash "^4.17.5"
30223032
minimist "^1.2.0"
@@ -3813,13 +3823,6 @@ get-package-type@^0.1.0:
38133823
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
38143824
integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
38153825

3816-
get-port@^2.1.0:
3817-
version "2.1.0"
3818-
resolved "https://registry.yarnpkg.com/get-port/-/get-port-2.1.0.tgz#8783f9dcebd1eea495a334e1a6a251e78887ab1a"
3819-
integrity sha1-h4P53OvR7qSVozThpqJR54iHqxo=
3820-
dependencies:
3821-
pinkie-promise "^2.0.0"
3822-
38233826
get-stdin@^6.0.0:
38243827
version "6.0.0"
38253828
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
@@ -5084,6 +5087,11 @@ json-schema-traverse@^0.4.1:
50845087
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
50855088
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
50865089

5090+
json-schema-traverse@^1.0.0:
5091+
version "1.0.0"
5092+
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
5093+
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
5094+
50875095
json-stable-stringify-without-jsonify@^1.0.1:
50885096
version "1.0.1"
50895097
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -6210,18 +6218,6 @@ pify@^4.0.1:
62106218
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
62116219
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
62126220

6213-
pinkie-promise@^2.0.0:
6214-
version "2.0.1"
6215-
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
6216-
integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
6217-
dependencies:
6218-
pinkie "^2.0.0"
6219-
6220-
pinkie@^2.0.0:
6221-
version "2.0.4"
6222-
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
6223-
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
6224-
62256221
pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5:
62266222
version "4.0.5"
62276223
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
@@ -6678,6 +6674,11 @@ require-directory@^2.1.1:
66786674
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
66796675
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
66806676

6677+
require-from-string@^2.0.2:
6678+
version "2.0.2"
6679+
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
6680+
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
6681+
66816682
require-main-filename@^2.0.0:
66826683
version "2.0.0"
66836684
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"

packages/core/src/__tests__/__helpers__/mockSegmentStore.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SEGMENT_DESTINATION_KEY } from '../../plugins/SegmentDestination';
12
import type { DeepLinkData, Storage } from '../../storage';
23
import type {
34
Context,
@@ -21,7 +22,9 @@ const INITIAL_VALUES: Data = {
2122
isReady: true,
2223
events: [],
2324
context: undefined,
24-
settings: {},
25+
settings: {
26+
[SEGMENT_DESTINATION_KEY]: {},
27+
},
2528
userInfo: {
2629
anonymousId: 'anonymousId',
2730
userId: undefined,

packages/core/src/__tests__/internal/fetchSettings.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { getMockLogger } from '../__helpers__/mockLogger';
22
import { SegmentClient } from '../../analytics';
33
import { MockSegmentStore } from '../__helpers__/mockSegmentStore';
4+
import { SEGMENT_DESTINATION_KEY } from '../../plugins/SegmentDestination';
45

56
describe('internal #getSettings', () => {
67
const defaultIntegrationSettings = {
7-
integrations: {},
8+
integrations: {
9+
// This one is injected by the mock
10+
[SEGMENT_DESTINATION_KEY]: {},
11+
},
812
};
913
const store = new MockSegmentStore();
1014

packages/core/src/analytics.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Unsubscribe } from '@segment/sovran-react-native';
22
import deepmerge from 'deepmerge';
33
import { AppState, AppStateStatus } from 'react-native';
4+
import { settingsCDN } from './constants';
45
import { getContext } from './context';
56
import {
67
applyRawEventData,
@@ -237,7 +238,7 @@ export class SegmentClient {
237238
}
238239

239240
async fetchSettings() {
240-
const settingsEndpoint = `https://cdn-settings.segment.com/v1/projects/${this.config.writeKey}/settings`;
241+
const settingsEndpoint = `${settingsCDN}/${this.config.writeKey}/settings`;
241242

242243
try {
243244
const res = await fetch(settingsEndpoint);

packages/core/src/constants.e2e.mock.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import { Platform } from 'react-native';
22
import type { Config } from '.';
33

44
export const batchApi = Platform.select({
5-
ios: 'http://localhost:9091',
6-
android: 'http://10.0.2.2:9091',
5+
ios: 'http://localhost:9091/events',
6+
android: 'http://10.0.2.2:9091/events',
7+
});
8+
9+
export const settingsCDN = Platform.select({
10+
ios: 'http://localhost:9091/settings',
11+
android: 'http://10.0.2.2:9091/settings',
712
});
813

914
export const defaultApiHost = 'api.segment.io/v1';

packages/core/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { Config } from './types';
33
export const batchApi = 'https://api.segment.io/v1/batch';
44
export const defaultApiHost = 'api.segment.io/v1';
55

6+
export const settingsCDN = 'https://cdn-settings.segment.com/v1/projects';
7+
68
export const defaultConfig: Config = {
79
writeKey: '',
810
flushAt: 20,

packages/core/src/plugin.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ export class DestinationPlugin extends EventPlugin {
100100

101101
timeline = new Timeline();
102102

103+
private hasSettings() {
104+
return this.analytics?.settings.get()?.[this.key] !== undefined;
105+
}
106+
107+
private isEnabled(event: SegmentEvent): boolean {
108+
let customerDisabled = false;
109+
if (event.integrations?.[this.key] === false) {
110+
customerDisabled = true;
111+
}
112+
113+
return this.hasSettings() && !customerDisabled;
114+
}
115+
103116
/**
104117
Adds a new plugin to the currently loaded set.
105118
@@ -141,6 +154,10 @@ export class DestinationPlugin extends EventPlugin {
141154
}
142155

143156
execute(event: SegmentEvent): SegmentEvent | undefined {
157+
if (!this.isEnabled(event)) {
158+
return undefined;
159+
}
160+
144161
// Apply before and enrichment plugins
145162
const beforeResult = this.timeline.applyPlugins({
146163
type: PluginType.before,

packages/core/src/plugins/__tests__/SegmentDestination.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ describe('SegmentDestination', () => {
5353
firebase: {
5454
someConfig: 'someValue',
5555
},
56+
[SEGMENT_DESTINATION_KEY]: {},
5657
},
5758
}),
5859
});
@@ -140,6 +141,7 @@ describe('SegmentDestination', () => {
140141
firebase: {
141142
someConfig: 'someValue',
142143
},
144+
[SEGMENT_DESTINATION_KEY]: {},
143145
},
144146
}),
145147
});
@@ -209,4 +211,33 @@ describe('SegmentDestination', () => {
209211
})),
210212
});
211213
});
214+
215+
it('lets plugins/events disable destinations individually', () => {
216+
const plugin = new SegmentDestination();
217+
// @ts-ignore
218+
plugin.analytics = new SegmentClient({
219+
...clientArgs,
220+
store: new MockSegmentStore({
221+
settings: {
222+
[SEGMENT_DESTINATION_KEY]: {},
223+
},
224+
}),
225+
});
226+
227+
const event: TrackEventType = {
228+
anonymousId: '3534a492-e975-4efa-a18b-3c70c562fec2',
229+
event: 'Awesome event',
230+
type: EventType.TrackEvent,
231+
properties: {},
232+
timestamp: '2000-01-01T00:00:00.000Z',
233+
messageId: '1d1744bf-5beb-41ac-ad7a-943eac33babc',
234+
context: { app: { name: 'TestApp' } },
235+
integrations: {
236+
[SEGMENT_DESTINATION_KEY]: false,
237+
},
238+
};
239+
240+
const result = plugin.execute(event);
241+
expect(result).toEqual(undefined);
242+
});
212243
});

0 commit comments

Comments
 (0)