Skip to content

Commit

Permalink
Consent: add bundled version that can be loaded via script tag (#938)
Browse files Browse the repository at this point in the history
  • Loading branch information
silesky authored Aug 28, 2023
1 parent 1b95946 commit 2191eb3
Show file tree
Hide file tree
Showing 22 changed files with 2,567 additions and 138 deletions.
7 changes: 7 additions & 0 deletions .changeset/angry-cows-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@segment/analytics-consent-wrapper-onetrust': minor
---

- Change API from oneTrust(analytics) -> withOneTrust(analytics). Allow withOneTrust(analytics).load(...).
- Add a umd bundle for snippet users

5 changes: 5 additions & 0 deletions .changeset/heavy-boxes-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@segment/analytics-consent-tools': patch
---

Have `createWrapper` return analytics instance to allow `.load` to be chained.
6 changes: 4 additions & 2 deletions examples/standalone-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"hoistingLimits": "workspaces"
},
"scripts": {
".": "yarn run -T turbo run --filter=@example/standalone-playground",
"dev": "yarn concurrently 'yarn run -T watch --filter=standalone-playground' 'sleep 10 && yarn http-server .'",
".": "yarn run -T turbo run --filter=@example/standalone-playground...",
"start": "yarn http-server . -o /pages",
"dev": "yarn concurrently 'yarn . build && yarn start' 'sleep 10 && yarn . watch'",
"concurrently": "yarn run -T concurrently"
},
"dependencies": {
"@segment/analytics-consent-wrapper-onetrust": "workspace:^",
"@segment/analytics-next": "workspace:^"
},
"devDependencies": {
Expand Down
165 changes: 165 additions & 0 deletions examples/standalone-playground/pages/index-consent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<html>

<head>
<style>
body {
font-family: monospace;
}

#event {
margin: 2em 0;
min-height: 200px;
min-width: 700px;
}
</style>

<form method="get">
<input type="text" name="writeKey" placeholder="Writekey" />
<button>Load</button>
</form>

<script src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js" type="text/javascript"
data-domain-script="80ca7b5c-e72f-4bd0-972a-b74d052a0820-test"></script>
<script
src="/node_modules/@segment/analytics-consent-wrapper-onetrust/dist/umd/analytics-onetrust.umd.development.js">
</script>

<script>
const { searchParams } = new URL(document.location);
const writeKey = searchParams.get("writeKey");
document.querySelector("input").value = writeKey;

if (writeKey) {
!(function () {
var analytics = (window.analytics = window.analytics || [])
if (!analytics.initialize)
if (analytics.invoked)
window.console &&
console.error &&
console.error('Segment snippet included twice.')
else {
analytics.invoked = !0
analytics.methods = [
'screen',
'register',
'deregister',
'trackSubmit',
'trackClick',
'trackLink',
'trackForm',
'pageview',
'identify',
'reset',
'group',
'track',
'ready',
'alias',
'debug',
'page',
'once',
'off',
'on',
'addSourceMiddleware',
'addIntegrationMiddleware',
'setAnonymousId',
'addDestinationMiddleware',
]
analytics.factory = function (e) {
return function () {
var t = Array.prototype.slice.call(arguments)
t.unshift(e)
analytics.push(t)
return analytics
}
}
for (var e = 0; e < analytics.methods.length; e++) {
var key = analytics.methods[e]
analytics[key] = analytics.factory(key)
}
analytics.load = function (key, e) {
var t = document.createElement('script')
t.type = 'text/javascript'
t.async = !0
t.src =
'https://cdn.segment.com/analytics.js/v1/' +
writeKey +
'/analytics.min.js'
var n = document.getElementsByTagName('script')[0]
n.parentNode.insertBefore(t, n)
analytics._loadOptions = e
}
analytics.SNIPPET_VERSION = '4.13.1'
analytics._writeKey = writeKey
withOneTrust(analytics).load()
analytics.page()
}
})()
}
</script>

</head>

<body>
<form>
<textarea name="event" id="event">
{
"name": "hi",
"properties": { },
"traits": { },
"options": { }
}
</textarea>
<div>
<button id="track">Track</button>
<button id="identify">Identify</button>
</div>
</form>

<pre id="logs"></pre>

<script type="text/javascript">
document.querySelector('#track').addEventListener('click', async (e) => {
e.preventDefault()
const contents = document.querySelector('#event').value
const evt = JSON.parse(contents)
const promise = window.analytics.track(
evt.name ?? '',
evt.properties ?? {},
evt.options ?? {}
)

promise?.then((ctx) => {
ctx.flush()
document.querySelector('#logs').textContent = JSON.stringify(
ctx.event,
null,
' '
)
})
})

document
.querySelector('#identify')
.addEventListener('click', async (e) => {
e.preventDefault()
const contents = document.querySelector('#event').value
const evt = JSON.parse(contents)
const promise = window.analytics.identify(
evt.name ?? '',
evt.properties ?? {},
evt.options ?? {}
)

promise?.then((ctx) => {
ctx.flush()
document.querySelector('#logs').textContent = JSON.stringify(
ctx.event,
null,
' '
)
})
})
</script>
</body>

</html>
3 changes: 3 additions & 0 deletions packages/config-webpack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @internal/config-webpack

This package is for sharing basic webpack configuration / browser support between all of the analytics.js artifacts in this monorepo.
19 changes: 19 additions & 0 deletions packages/config-webpack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@internal/config-webpack",
"version": "0.0.0",
"private": true,
"packageManager": "[email protected]",
"devDependencies": {
"@babel/cli": "^7.22.10",
"@babel/core": "^7.22.11",
"@babel/preset-env": "^7.22.10",
"@babel/preset-typescript": "^7.22.11",
"@types/circular-dependency-plugin": "^5",
"babel-loader": "^8.0.0",
"circular-dependency-plugin": "^5.2.2",
"terser-webpack-plugin": "^5.1.4",
"webpack": "^5.76.0",
"webpack-cli": "^4.8.0",
"webpack-merge": "^5.9.0"
}
}
71 changes: 71 additions & 0 deletions packages/config-webpack/webpack.config.common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const TerserPlugin = require('terser-webpack-plugin')
const CircularDependencyPlugin = require('circular-dependency-plugin')

const isProd = process.env.NODE_ENV === 'production'
const isWatch = process.env.WATCH === 'true'

/**
* This is a base webpack config that is used for all generic web packages.
* It should contain the same support as analytics.js (e.g. es5, minified, etc)
*
*
* @type { import('webpack').Configuration }
*/
module.exports = {
devtool: 'source-map',
stats: isWatch ? 'errors-warnings' : 'normal',
mode: isProd ? 'production' : 'development',
module: {
rules: [
{
test: /\.(ts|js)$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-typescript'],
[
'@babel/preset-env',
{
targets: {
ie: 11,
},
},
],
],
},
},
],
exclude: /node_modules/,
},
],
},
optimization: {
moduleIds: 'deterministic',
minimize: isProd,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
// not sure why, if we target es5, we can set ecma to 2015 here without breaking ie11.
// this from the original webpack config had that netto set up, so leaving it as is.
ecma: '2015',
mangle: true,
compress: true,
output: {
comments: false,
},
},
}),
],
},
resolve: {
extensions: ['.ts', '.js'],
},
plugins: [
new CircularDependencyPlugin({
failOnError: true,
}),
],
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { AnalyticsBrowser } from '@segment/analytics-next'
import { oneTrust } from '@segment/analytics-consent-wrapper-onetrust'
import { withOneTrust } from '@segment/analytics-consent-wrapper-onetrust'

export const analytics = new AnalyticsBrowser()

oneTrust(analytics, {
withOneTrust(analytics, {
disableConsentChangedEvent: false,
integrationCategoryMappings: {
Fullstory: ['C0001'],
'Actions Amplitude': ['C0004'],
},
})
}).load({ writeKey: '9lSrez3BlfLAJ7NOChrqWtILiATiycoc' })
;(window as any).analytics = analytics

analytics.load({ writeKey: '9lSrez3BlfLAJ7NOChrqWtILiATiycoc' })
void window.analytics.page().then(console.log)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import { oneTrust } from '@segment/analytics-consent-wrapper-onetrust'
import { withOneTrust } from '@segment/analytics-consent-wrapper-onetrust'

oneTrust(window.analytics, {
withOneTrust(window.analytics, {
integrationCategoryMappings: {
Fullstory: ['C0001'],
'Actions Amplitude': ['C0004'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ describe(createWrapper, () => {
expect(addSourceMiddlewareSpy).toBeCalledWith(expect.any(Function))
})

it('should be chainable', async () => {
await wrapTestAnalytics().load(DEFAULT_LOAD_SETTINGS)
const { args } = getAnalyticsLoadLastCall()
expect(args.length).toBeTruthy()
})

describe('shouldLoad', () => {
describe('Throwing errors / aborting load', () => {
const createShouldLoadThatThrows = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ type Extends<T, U> = T extends U ? true : false
const f: Extends<AnalyticsSnippet, AnyAnalytics> = true
const g: Extends<AnalyticsBrowser, AnyAnalytics> = true
console.log(f, g)

// should be chainable
wrap({} as AnalyticsBrowser).load({ writeKey: 'foo' })
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const createWrapper: CreateWrapper = (createWrapperOptions) => {
})
}
analytics.load = loadWithConsent
return analytics
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/consent/consent-tools/src/types/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export interface AnyAnalytics {
**/
// Why type this as 'object' rather than 'AnyAnalytics'? IMO, the chance of a false positive is much higher than the chance that someone will pass in an object that is not an analytics instance.
// We have an assertion function that throws an error if the analytics instance is not compatible.
export type Wrapper = (analyticsInstance: object) => void
export type Wrapper = <Analytics extends object>(
analyticsInstance: Analytics
) => Analytics

/**
* Create a function which wraps analytics instances to add consent management.
Expand Down
Loading

0 comments on commit 2191eb3

Please sign in to comment.