Skip to content

Commit

Permalink
Merge pull request #1 from KatalysatorAB/dictionary-tags
Browse files Browse the repository at this point in the history
Dictionary tags
  • Loading branch information
erkie committed Jun 9, 2016
2 parents b839a92 + e0d9a7a commit 5d14578
Show file tree
Hide file tree
Showing 7 changed files with 425 additions and 17 deletions.
81 changes: 77 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,34 @@ The current version can be accessed from here:

### .getTags

Fetch all tags associated with the current browser client.
`void Glimr.getTags( string clientId, function(array, object) callback )`

Fetch all tags associated with the current browser client. The callback should accept 1 array and 1 object parameter. Depending on the version of the Glimr API used, the object parameter might be empty.

```js
Glimr.getTags("YOUR_CLIENT_ID", function(tags) {
Glimr.getTags("YOUR_CLIENT_ID", function(tags, tagMappings) {
console.log("Tags for client:", tags);
console.log("... :", tagMappings);
});
```

The object is simply the array of tags but with mappings of meanings. The response might be:

```js
tags = ["apple", "banana", "orange"];
tagMappings = {
yellow: ["banana", "orange"],
red: ["apple"]
};
```

_Note:_ The `getTags`-call is cached for the duration of the page load. So calling it multiple times will only result in one call to the Glimr servers. The cache is cleared on page refresh.

### .setTagCacheTimeInSeconds

`number Glimr.setTagCacheTimeInSeconds( number desiredSecondsToCache )`
`number Glimr.getTagCacheTimeInSeconds( )`

The tags from `Glimr.getTags` can be cached for a duration of up to 5 minutes. The tags don't change too often so it's a good idea to limit network traffic on the client.

```js
Expand All @@ -39,6 +55,8 @@ The tags are currently only stored in `localStorage`. If `localStorage` is not a

### .getCachedURLTags

`array Glimr.getCachedURLTags( string clientId )`

Glimr crawls your web property and knows which URL indicates a set of tags, based on filters you setup in the dashboard. The Glimr SDK can download and cache this database in an efficiant manner, and you can use it to get tags without having to make a network request. To fetch the tags associated with the current browser URL you call `Glimr.getCachedURLTags` which does a fast synchronous lookup.

```js
Expand All @@ -50,11 +68,66 @@ console.log("Cached tags", tags);

### .getTagsAndPushToDataLayer

`void Glimr.getTagsAndPushToDataLayer( string clientId )`

Does the same calls as `Glimr.getTags`, but instead of a callback it simply pushes the tags to the global variable named `dataLayer`, which Google Tag Manager uses.

```js
var tags = Glimr.getCachedURLTags("YOUR_CLIENT_ID");
console.log("Cached tags", tags);
Glimr.getTagsAndPushToDataLayer("YOUR_CLIENT_ID");
```

## Sending tags to an ad server

Your glimr tags will live very happily in your ad server. To get there, they need to be encoded in a compliant manner. This library provides some tools to make that easy.

The biggest hurdle is how to encode your tags. Since they might come back as an array of strings, or a dictionary of arrays, we have tools to encode both into HTTP spec compliant query strings.

### .objectToQuery

`string Glimr.objectToQuery( object value )`

Take an object/dictionary and convert to a query string. It will not modify array keys (postfix them with `[]`), that is up to the implementer to make sure the keys are postfix'd.

```javascript
var tags = {
key1: "value",
key2: ["foo", "bar"],
"key3[]": ["baz", "bam"]
};

var queryString = Glimr.objectToQuery(tags);

// key1=value&key2=foo&key2=bar&key3%5B%5D=baz&key3%5B%5D=bam
```

### .arrayToQuery

`string Glimr.arrayToQuery( array value, string key )`

Take an array and convert to a query string. The second argument is a string of which will become key for the values.

It will not modify keys like other frameworks might (i.e postfix them with `[]`), that is up to the implementer to make sure the keys are postfix'd.

```javascript
var tags = ["a", "bcd", "ef"];

var queryString = Glimr.arrayToQuery(tags, "my_key");

// my_key=a&my_key=bcd&my_key=ef
```

### .escapeStringForQuery

`string Glimr.escapeStringForQuery( string value )`

This is more of a helper that might be useful for very custom stuff. It's what `Glimr.objectToQuery` and `Glimr.arrayToQuery` use to encode the values.

Usage is easy:

```javascript
var escapedString = Glimr.escapeStringForQuery("hello world");

// hello%20world
```

## Google Tag Manager
Expand Down
2 changes: 1 addition & 1 deletion dist/glimr.min.js

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions spec/SerializationSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"use strict";

describe('serialization', function(){
it('should serialize array tags', function() {
// Setup
var tags = ["a", "b", "c"];

// Act
var queryString = Glimr.arrayToQuery(tags, "key");

// Verify
expect(queryString).toBe("key=a&key=b&key=c");
});

it('should serialize dictionary tags', function() {
// Setup
var tags = {
key1: ["a", "b"],
key2: ["c"]
};

// Act
var queryString = Glimr.objectToQuery(tags);

// Verify
expect(queryString).toBe("key1=a&key1=b&key2=c");
});

it('should serialize mixed dictionary array tags', function() {
// Setup
var tags = {
key1: ["a", "b"],
key2: ["c"],
key3: "hello world"
};

// Act
var queryString = Glimr.objectToQuery(tags);

// Verify
expect(queryString).toBe("key1=a&key1=b&key2=c&key3=hello%20world");
});

it('should escape weird characters', function() {
// Setup
var objectDictionaryArray = {
key1: ["& []?'\"", "&åäö"],
"key2[]": ["123"],
" ö %&": ["123"]
};

var objectDictionary = {
key1: "& []?",
"key2[]": "123"
};

var arrayValues = ["& []?", "132&?"];

// Act
var objectDictionaryArrayString = Glimr.objectToQuery(objectDictionaryArray);
var objectDictionaryString = Glimr.objectToQuery(objectDictionary);
var arrayValuesString = Glimr.arrayToQuery(arrayValues, "key");

// Verify
expect(objectDictionaryArrayString).toBe("key1=%26%20%5B%5D%3F'%22&key1=%26%C3%A5%C3%A4%C3%B6&key2%5B%5D=123&%20%C3%B6%20%25%26=123");
expect(objectDictionaryString).toBe("key1=%26%20%5B%5D%3F&key2%5B%5D=123");
expect(arrayValuesString).toBe("key=%26%20%5B%5D%3F&key=132%26%3F");
});

it('should be able to deserialize into an object', function() {
// Setup
var complexQueryString = "key1=%26%20%5B%5D%3F'%22&key1=%26%C3%A5%C3%A4%C3%B6&key2%5B%5D=123&%20%C3%B6%20%25%26=123";

// Act
var data = Glimr.queryToObject(complexQueryString);

// Verify
expect(data["key1"]).toEqual(["& []?'\"", "&åäö"]);
expect(data["key2[]"]).toEqual(["123"]);
expect(data[" ö %&"]).toEqual(["123"]);
});
});
1 change: 0 additions & 1 deletion spec/TagCacheSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ describe('tag_cache', function() {
});
});


it("should keep tags separate by pixelId", function() {
var isDone1 = false;
var isDone2 = false;
Expand Down
115 changes: 114 additions & 1 deletion spec/TagsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ describe('tags', function(){
});
});

it('should fetch a dictionary of tags for a publisher id', function() {
var isDone = false;
var tagMappings;

runs(function() {
Glimr.getTags("with_dictionary_key_values", function(x, fetchedTagMappings) {
tagMappings = fetchedTagMappings;
isDone = true;
});
});

waitsFor(function() {
return isDone;
});

runs(function() {
expect(tagMappings.key1).toContain("apple");
expect(tagMappings.key1).toContain("banana");
expect(tagMappings.key2).toBe("orange");
expect(tagMappings.key3).toContain("sugar");
});
});

it('should fetch an empty list of tags when no tags are found', function() {
var isDone = false;
var tags;
Expand Down Expand Up @@ -94,7 +117,7 @@ describe('tags', function(){
});
});

it('should return thing by calling it twice in succession', function() {
it('should return the same array by calling it twice in succession', function() {
var isDone = false;
var tags;

Expand Down Expand Up @@ -139,6 +162,51 @@ describe('tags', function(){
});
});

it('should return the same dictionary by calling it twice in succession', function() {
var isDone = false;
var tagMappings;

runs(function() {
Glimr.getTags("with_dictionary_key_values", function(x, fetchedTagMappings) {
tagMappings = fetchedTagMappings;
isDone = true;
});
});

waitsFor(function() {
return isDone;
});

runs(function() {
expect(tagMappings.key1).toContain("apple");
expect(tagMappings.key1).toContain("banana");
expect(tagMappings.key2).toBe("orange");
expect(tagMappings.key3).toContain("sugar");

isDone = false;
});

runs(function() {
Glimr.getTags("with_dictionary_key_values", function(_, fetchedTagMappings) {
tagMappings = fetchedTagMappings;
isDone = true;
});
});

waitsFor(function() {
return isDone;
});

runs(function() {
expect(tagMappings.key1).toContain("apple");
expect(tagMappings.key1).toContain("banana");
expect(tagMappings.key2).toBe("orange");
expect(tagMappings.key3).toContain("sugar");

isDone = false;
});
});

it('should return thing by calling it twice async', function() {
var isDone1 = false;
var isDone2 = false;
Expand Down Expand Up @@ -263,4 +331,49 @@ describe('tags', function(){
expect(networkRequests).toEqual(2);
});
});

it('should be able to fetch dictionary tags from cache', function() {
var isDone = false;
var tagMappings;

// Fetch tags normally
runs(function() {
Glimr.setTagCacheTimeInSeconds(300);

Glimr.getTags("with_dictionary_key_values", function(fetchedTags) {
isDone = true;
});
});

waitsFor(function() {
return isDone;
});

// Try to fetch tags when server crashed
runs(function() {
setupGlimrCrashedServer();
Glimr.state = {};
Glimr.initialize();

isDone = false;

Glimr.setTagCacheTimeInSeconds(300);
Glimr.getTags("with_dictionary_key_values", function(_, fetchedTagMappings) {
isDone = true;
tagMappings = fetchedTagMappings;
});
});

waitsFor(function() {
return isDone;
});

runs(function() {
expect(tagMappings.key1).toContain("apple");
expect(tagMappings.key1).toContain("banana");
expect(tagMappings.key2).toContain("orange");
expect(tagMappings.key3).toContain("sugar");
});
});

});
8 changes: 8 additions & 0 deletions spec/mock_responses/with_dictionary_key_values.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tags": ["apple", "banana", "orange", "sugar"],
"mapping": {
"key1": ["apple", "banana"],
"key2": "orange",
"key3": ["sugar"]
}
}
Loading

0 comments on commit 5d14578

Please sign in to comment.