Skip to content

Commit

Permalink
Merge pull request #1 from tabnine/feature/initial-version
Browse files Browse the repository at this point in the history
DEV2-5321 Add RudderStack GitBook Integration
  • Loading branch information
nimrod-codota authored Mar 12, 2024
2 parents 0ed9713 + d20e19b commit ce94522
Show file tree
Hide file tree
Showing 21 changed files with 5,754 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module.exports = {
env: {
es2021: true,
node: true
},
extends: [
'airbnb-typescript/base',
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:import/errors',
'plugin:import/typescript',
'prettier'
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname
},
plugins: ['@typescript-eslint', 'import'],
rules: {
'no-void': 'off',
'no-console': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/camelcase': 'off',
'class-methods-use-this': 'off',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'**/*.test.[t|j]s',
'**/*.spec.[t|j]s',
'./src/app/tests/**/*.[t|j]s'
]
}
]
}
};
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# The following github team will be considered code-owners for all account repositories
# code-owners will be later applied as merge approvers for all pull requests in order to comply with our SOC2 Policy.
* @codota/merge-approvers
39 changes: 39 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: 'CI'

on:
push:
branches: ['main']
pull_request:

jobs:
build:
name: 'Build'
runs-on: ubuntu-latest
steps:
- name: Checkout project
uses: actions/checkout@v2

- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: '18.16'

- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: .yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: yarn --immutable

- name: Prettier
run: yarn prettier:check

- name: Lint
run: yarn lint

- name: Test
run: yarn test
21 changes: 21 additions & 0 deletions .github/workflows/enforce-pr-jira-association.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Enforce PR-Jira association

on:
pull_request:
types:
- opened
- reopened
- edited
- synchronize

jobs:
enforce-issue:
runs-on: ubuntu-latest
name: JIRA Association
steps:
- name: Check for JIRA ISSUE
id: check
uses: usehaystack/jira-pr-link-action@v4
with:
ignore-author: dependabot[bot]
project: 'DEV2'
18 changes: 18 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Release

on: workflow_dispatch

jobs:
publish:
name: Publish to GitBook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.x
- run: npm ci
- run: npm install -g @gitbook/cli
- run: gitbook publish .
env:
GITBOOK_TOKEN: ${{ secrets.GITBOOK_TOKEN }}
1 change: 1 addition & 0 deletions .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_
5 changes: 5 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn prettier
yarn lint:fix
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18.16.0
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# @gitbook/integration-segment

## 1.0.0

### Major Changes

### Minor Changes

### Patch Changes
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# gitbook-integration-rudderstack

A Gitbook plugin for sending events to RudderStack
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { presets: ['@babel/preset-env'] };
44 changes: 44 additions & 0 deletions gitbook-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: rudderstack
title: RudderStack
icon: ./assets/icon.png
organization: tabnine
description: Integrate your GitBook public documentation with your RudderStack pipeline.
visibility: private
script: ./src/index.ts
scopes:
- space:views:read
summary: |
# Overview
The GitBook Segment integration empowers your teams by connecting your GitBook public documentation to your Segment pipeline.
# How it works
Automatic page view events on your public documentation:
Each of your connected GitBook spaces will send a `GitBook Space Viewed` event to the RudderStack source associated via id.
Track your users' actions across all your company's websites:
- Each `GitBook Space Viewed` includes the `anonymousId` property extracted from the RudderStack cookie.
- If the active user has already visited one of your other company's websites, the `anonymousId` will match the one already set by RudderStack.
- Otherwise, GitBook will associate a new `anonymousId` for the user matching RudderStack's format.
# Configure
To install the RudderStack integration on a single space click on the Integrations button in sub-navigation. Alternatively, you can install it on multiple or all spaces by going into your organization settings.
You will need the RudderStack Source Write Key to help you identify the data source, and the Data Plane URL for your RudderStack dataplane.
categories:
- analytics
configurations:
space:
properties:
data_plane_url:
type: string
title: Data Plane URL
description: The URL address of the RudderStack Data Plane.
source_write_key:
type: string
title: Write Key
description: The write key associated with the event source.
required:
- data_plane_url
- source_write_key
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
'^.+\\.(js|jsx)$': 'babel-jest'
}
};
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "integration-rudderstack",
"version": "1.0.2",
"private": true,
"dependencies": {
"@gitbook/api": "*",
"@gitbook/runtime": "*",
"crypto-js": "^4.2.0"
},
"devDependencies": {
"@babel/preset-env": "^7.23.9",
"@gitbook/cli": "*",
"@gitbook/eslint-config": "*",
"@gitbook/tsconfig": "*",
"@types/crypto-js": "^4.2.2",
"@types/jest": "^29.5.12",
"@types/node": "^18.16.0",
"assert": "^2.0.0",
"babel-jest": "^29.7.0",
"esbuild-register": "^3.5.0",
"eslint": "^8.57.0",
"eslint-config-airbnb-typescript": "7",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "2",
"eslint-plugin-json": "^3.1.0",
"husky": "^9.0.11",
"jest": "^29.7.0",
"test": "^3.2.1",
"ts-jest": "^29.1.2",
"typescript": "^5.3.3"
},
"scripts": {
"typecheck": "tsc --noEmit",
"publish-integration": "gitbook publish .",
"test": "jest --passWithNoTests",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint --fix .",
"prepare": "husky install",
"prettier": "prettier --write .",
"prettier:check": "prettier --check ."
},
"prettier": {
"useTabs": false,
"trailingComma": "none",
"singleQuote": true,
"jsxBracketSameLine": false,
"arrowParens": "avoid"
},
"eslintIgnore": [
"*.test.ts",
".eslintrc.js"
]
}
81 changes: 81 additions & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as api from '@gitbook/api';
import { AES, enc } from 'crypto-js';

import { version } from '../package.json';

const RUDDER_ANONYMOUS_ID_COOKIE_NAME = 'rl_anonymous_id';

/**
* Generate the event to track in RudderStack for an actual GitBook event.
*/
export default function generateRudderStackTrackEvent(
event: api.SpaceViewEvent
) {
const { visitor, referrer, url, spaceId, pageId } = event;

const anonymousId = getAnonymousId(event);
const visitedURL = new URL(url);
return {
event: 'GitBook Space Viewed',
anonymousId,
context: {
library: {
name: 'GitBook',
version
},
page: {
path: visitedURL.pathname,
search: visitedURL.search,
url,
referrer
},
userAgent: visitor.userAgent,
ip: visitor.ip
},
properties: {
spaceId,
pageId
}
};
}

/**
* Return the anonymous ID we send to RudderStack in the Track event.
*
* Retrieve the value from the `rl_anonymous_id` RudderStack cookie if present.
* This allows to consolidate the track event with other events generated by an anonymous user
* that already has visited the customer website (where RudderStack tracking is setup).
*
* When there is no `rl_anonymous_id` cookie, we fallback to using the GitBook anonymous ID.
*/
function getAnonymousId(event: api.SpaceViewEvent): string {
const { visitor } = event;
const { cookies } = visitor;

const extractedAnonymousId = extractAnonymousId(cookies);
console.log('extracted anonymous id', extractedAnonymousId);
return extractedAnonymousId || visitor.anonymousId;
}

function decrypt(urlDecodedCookieValue: string): string {
return AES.decrypt(
urlDecodedCookieValue.substring('RudderEncrypt:'.length),
'Rudder'
).toString(enc.Utf8);
}

function extractAnonymousId(cookies: { [p: string]: string }): string {
let anonymousId: string;

if (cookies) {
const rudderAnonymousId = cookies[RUDDER_ANONYMOUS_ID_COOKIE_NAME];
if (rudderAnonymousId) {
const decodedAnonymousId = decodeURIComponent(rudderAnonymousId);
const decryptedId = decrypt(decodedAnonymousId);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
anonymousId = JSON.parse(decryptedId);
}
}

return anonymousId;
}
50 changes: 50 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
createIntegration,
RuntimeContext,
RuntimeEnvironment
} from '@gitbook/runtime';

import generateRudderStackTrackEvent from './events';

type RudderStackRuntimeContext = RuntimeContext<
RuntimeEnvironment<
// eslint-disable-next-line @typescript-eslint/ban-types
{},
{
data_plane_url?: string;
source_write_key?: string;
}
>
>;

export default createIntegration<RudderStackRuntimeContext>({
events: {
space_view: async (event, { environment }) => {
const writeKey =
environment.spaceInstallation?.configuration.source_write_key;
if (!writeKey) {
throw new Error(
`The RudderStack source write key is missing from the Space (ID: ${event.spaceId}) installation.`
);
}

const dataPlaneUrl =
environment.spaceInstallation?.configuration.data_plane_url;
if (!dataPlaneUrl) {
throw new Error(
`The RudderStack data plane URL is missing from the Space (ID: ${event.spaceId}) installation.`
);
}

const trackEvent = generateRudderStackTrackEvent(event);
await fetch(`${dataPlaneUrl}/v1/track`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${btoa(`${writeKey}:`)}`
},
body: JSON.stringify(trackEvent)
});
}
}
});
Loading

0 comments on commit ce94522

Please sign in to comment.