/sample-user-transformers

Sample User Transformations for RudderStack - the CDP for Developers.

Primary LanguageJavaScriptMIT LicenseMIT

Sample RudderStack Transformations

RudderStack's Transformations feature gives you the ability to code custom JavaScript functions to implement specific use-cases on your event data. This repository contains some useful transformation templates that you can use to create your own transformations.

For more information on RudderStack Transformations, refer to the documentation.

Table of contents

Getting started

The sample transformations and libraries included in this repository can be added via the RudderStack dashboard. Follow these steps:

  1. In the RudderStack dashboard, go to Enhance > Transformations.
  2. Click New Transformations.
  3. Enter a name and description and add the code for the transformation or library.
  4. Click Save.

For detailed steps on adding a new transformation or library, refer to the documentation.

Filtering

Denylist Event Names

Filter out event if a property (event name in this example) is included in a denylist.

  1. Drop event if denylist includes event name
  2. Return event otherwise
export function transformEvent(event, metadata) {
    const eventNames = ["game_load_time", "lobby_fps"];
    const eventName = event.event;
    if (eventName && eventNames.includes(eventName)) return;
    return event;
}
Example Input and Output
Input Output
{
 "event": "game_load_time"
}
{
 "event": "players"
}
{
 "event": "players"
}

Allowlist email domains

Filter out event if a property (email domain in this example) is not included in a allowlist.

  1. Return event if allowlist includes email domain
  2. Drop event otherwise
export function transformEvent(event, metadata) {
    const domains = ["rudderstack.com", "rudderlabs.com"];
    const email = event.context?.traits?.email;
    if (email && domains.includes(email.split("@").pop())) return event;
    return;
}
Example Input and Output
Input Output
{
 "context": {
  "traits": {
   "email": "john@gmail.com"
  }
 }
}
{
 "context": {
  "traits": {
   "email": "john@rudderstack.com"
  }
 }
}
{
 "context": {
  "traits": {
   "email": "john@rudderstack.com"
  }
 }
}

Sampling

User-based

Drop a random sample of events based on a property (user ID in this example)

  1. Import cyrb53 function from hash library
  2. Drop event if remainder of hashed user ID less than 5
  3. Return event otherwise
import { cyrb53 } from "hash";

export function transformEvent(event, metadata) {
    const userId = event.userId;
    if (userId && cyrb53(userId) % 10 < 5) return;
    return event;
}
Example Input and Output
Input Output
{
 "userId": "54321"
}
{
 "userId": "12345"
}
{
 "userId": "12345"
}

Enrichment

Geolocation data

Enrich event with geolocation data using an external API and IP address

  1. Fetch geolocation data from external IP2Location API
  2. Add data to event
  3. Return event
export async function transformEvent(event, metadata) {
    if (event.request_ip) {
        const res = await fetch("<YOUR_API_ENDPOINT>" + event.request_ip); // Use your paid IP-to-geolocation API endpoint.
        event.context.geolocation = res;
    }
    return event;
}
Example Input and Output
Input Output
{
 "context": {
  "ip": "64.233.160.0"
 }
}
{
 "context": {
  "ip": "64.233.160.0",
  "geolocation": {
   "country_code": "US",
   "country_name": "United States",
   "region_name": "California",
   "city_name": "Mountain View",
   "latitude": "37.405992",
   "longitude": "-122.078515",
   "zip_code": "94043",
   "time_zone": "-07:00"
  }
 }
}

User data

Enrich event with user data using an external API and email address

  1. Get user data from external Clearbit API
  2. Add data to event's traits
  3. Return event
export async function transformEvent(event) {
    const email = event.context?.traits?.email;
    if (email) {
        const res = await fetch("https://person.clearbit.com/v2/combined/find?email=" + email, {
            headers: {
                "Authorization": "Bearer <your_clearbit_secure_key"
            }
        });
        event.context.traits.enrichmentInfo = res;
    }
    return event;
}
Example Input and Output
Input Output
{
 "context": {
  "traits": {
   "email": "alex@alexmaccaw.com"
  }
 }
}
{
 "context": {
  "traits": {
   "email": "john@gmail.com",
   "enrichmentInfo": {
    "id": "d54c54ad-40be-4305-8a34-0ab44710b90d",
    "name": {
     "fullName": "Alex MacCaw",
     "givenName": "Alex",
     "familyName": "MacCaw"
    },
    "email": "alex@alexmaccaw.com",
    "location": "San Francisco, CA, US",
    "timeZone": "America/Los_Angeles",
    "utcOffset": -8,
    "geo": {
     "city": "San Francisco",
     "state": "California",
     "stateCode": "CA",
     "country": "United States",
     "countryCode": "US",
     "lat": 37.7749295,
     "lng": -122.4194155
    },
    "bio": "O'Reilly author, software engineer & traveller. Founder of https://clearbit.com",
    "site": "http://alexmaccaw.com",
    "avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d54c54ad-40be-4305-8a34-0ab44710b90d",
    "employment": {
     "domain": "clearbit.com",
     "name": "Clearbit",
     "title": "Co-founder, CEO",
     "role": "leadership",
     "subRole": "ceo",
     "seniority": "executive"
    },
    "facebook": {
     "handle": "amaccaw"
    },
    "github": {
     "handle": "maccman",
     "avatar": "https://avatars.githubusercontent.com/u/2142?v=2",
     "company": "Clearbit",
     "blog": "http://alexmaccaw.com",
     "followers": 2932,
     "following": 94
    },
    "twitter": {
     "handle": "maccaw",
     "id": "2006261",
     "bio": "O'Reilly author, software engineer & traveller. Founder of https://clearbit.com",
     "followers": 15248,
     "following": 1711,
     "location": "San Francisco",
     "site": "http://alexmaccaw.com",
     "avatar": "https://pbs.twimg.com/profile_images/1826201101/297606_10150904890650705_570400704_21211347_1883468370_n.jpeg"
    },
    "linkedin": {
     "handle": "pub/alex-maccaw/78/929/ab5"
    },
    "googleplus": {
     "handle": null
    },
    "gravatar": {
     "handle": "maccman",
     "urls": [
      {
       "value": "http://alexmaccaw.com",
       "title": "Personal Website"
      }
     ],
     "avatar": "http://2.gravatar.com/avatar/994909da96d3afaf4daaf54973914b64",
     "avatars": [
      {
       "url": "http://2.gravatar.com/avatar/994909da96d3afaf4daaf54973914b64",
       "type": "thumbnail"
      }
     ]
    },
    "fuzzy": false,
    "emailProvider": false,
    "indexedAt": "2016-11-07T00:00:00.000Z"
   }
  }
 }
}

User agent data

Enrich event with parsed user agent data

  1. Import UAParser function from user agent parser library
  2. Add parsed user agent data
  3. Return event
import { UAParser } from "userAgentParser";

export function transformEvent(event, metadata) {
    const userAgent = event.context?.userAgent;
    if (userAgent) {
        const parser = new UAParser();
        const parsedUserAgent = parser.setUA(userAgent).getResult();
        event.context.parsedUserAgent = parsedUserAgent;
    }
    return event;
}
Example Input and Output
Input Output
{
 "context": {
  "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Firefox/98.0"
 }
}
{
 "context": {
  "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Firefox/98.0",
  "parsedUserAgent": {
   "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Firefox/98.0",
   "browser": {
    "name": "Firefox",
    "version": "98.0",
    "major": "98"
   },
   "engine": {
    "name": "Gecko",
    "version": "98.0"
   },
   "os": {
    "name": "Mac OS",
    "version": "10.15"
   },
   "device": {},
   "cpu": {}
  }
 }
}

Dynamic header

Add a dynamic header to event payload

  1. Add dynamnic header to event
  2. Return event
export function transformEvent(event, metadata) {
    event.header = {
        dynamic_header_1: "dynamic_header_1_value",
        dynamic_header_2: "dynamic_header_2_value"
    };
    return event;
}
Example Input and Output
Input Output
{
 "event": "Button Clicked"
}
{
 "event": "Button Clicked",
 "header": {
  "dynamic_header_1": "dynamic_header_1_value",
  "dynamic_header_2": "dynamic_header_2_value"
 }
}

Dynamic path

Dynamically append the event endpoint

  1. Add dynamic path to event's endpoint base URL
  2. Return event
export function transformEvent(event, metadata) {
    const email = event.context?.traits?.email;
    if (email) event.appendPath = `/search?email=${email}`;
    return event;
}
Example Input and Output
Input Output
{
 "context": {
  "traits": {
   "email": "john@gmail.com"
  }
 }
}
{
 "context": {
  "traits": {
   "email": "john@gmail.com"
  }
 },
 "appendPath": "/search?email=john@gmail.com"
}

Masking

Replace PII

Fuzzy find and replace PII properties (social security number in this example)

  1. Import walk function from fuzzy find replace library
  2. Replace values where keys approximately match target keys
  3. Return event
import { walk } from "fuzzyFindReplace";

export function transformEvent(event, metadata) {
    const targetKeys = [
        "SSN",
        "Social Security Number",
        "social security no.",
        "social sec num",
        "ssnum"
    ];
    walk(event, targetKeys, "XXX-XX-XXXX");
    return event;
}
Example Input and Output
Input Output
{
 "context": {
  "traits": {
   "socialSecurityNumber": "123-45-6789"
  }
 }
}
{
 "context": {
  "traits": {
   "socialSecurityNumber": "XXX-XX-XXXX"
  }
 }
}

Cleaning

Remove null properties

Remove all properties with null values

  1. Remove properties with null values
  2. Return event
export function transformEvent(event) {
    if (event.properties) {
        const keys = Object.keys(event.properties);
        if (keys) {
            keys.forEach(key => {
                if (event.properties[key] === null) delete event.properties[key];
            })
        }
    }
    return event;
}
Example Input and Output
Input Output
{
 "properties": {
  "revenue": null,
  "quantity": 4
 }
}
{
 "properties": {
  "quantity": 4
 }
}

Miscellaneous

Source ID

Perform action if event is from a specific source

  1. Do something if event from specified source
  2. Return event
export function transformEvent(event, metadata) {
    if (metadata(event).sourceId === "12345") {
        // Do something
    }
    return event;
}

Change event type

Change the type of an event (track to identify in this example)

  1. Change event from track to identify if conditions are met
  2. Return event
export function transformEvent(event) {
    let updatedEvent = event;
    if (
        event.type === "track" && 
        event.event === "ide-authentication" && 
        event.properties?.email && 
        event.properties.email !== ""
    ) {
        updatedEvent.type = "identify";
        let updatedContext = event.context || {}; 
        updatedContext.traits = updatedContext.traits || {};
        updatedContext.traits.email = event.properties.email;
        updatedEvent.context = updatedContext;
    }
   return updatedEvent;
}
Example Input and Output
Input Output
{
 "type": "track",
 "event": "ide-authentication",
 "properties": {
  "email": "john@gmail.com"
 }
}
{
 "type": "identify",
 "event": "ide-authentication",
 "properties": {
  "email": "john@gmail.com"
 },
 "context": {
  "traits": {
   "email": "john@gmail.com"
  }
 }

Batch

Perform action for each event in a batch

  1. Do something for each event
  2. Return events
export function transformBatch(events, metadata) {
    events.forEach(event => {
        // Do something
    });
    return events;
}

License

MIT