Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.

Commit 95e7fb6

Browse files
committed
initialize the journey builder custom hello world repo
0 parents  commit 95e7fb6

File tree

731 files changed

+69079
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

731 files changed

+69079
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
*.swp
3+
*.swo
4+
*.log

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Journey Builder
2+
## Custom Interaction
3+
###Hello World
4+
5+
**NOTE:** You won't be able to run this locally. It is intended to be ran on a publicly available web server/cloud only.
6+
7+
**NOTE:** This app and the associated code is NOT production quality, its pure purpose is to demonstrate the full flow of custom interactions in Journey Builder
8+
9+

app.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
'use strict';
2+
// Module Dependencies
3+
// -------------------
4+
//require('newrelic');
5+
var express = require('express');
6+
var fs = require('fs');
7+
var http = require('http');
8+
var JWT = require('./lib/jwtDecoder');
9+
var path = require('path');
10+
var request = require('request');
11+
var routes = require('./routes');
12+
var activity = require('./routes/activity');
13+
var trigger = require('./routes/trigger');
14+
15+
var app = express();
16+
17+
// Register configs for the environments where the app functions
18+
// , these can be stored in a separate file using a module like config
19+
var APIKeys = {
20+
appId : '__insert_your_app_id__',
21+
clientId : '__insert_your_app_client_id__',
22+
clientSecret : '__insert_your_app_client_secret__',
23+
appSignature : '__insert_your_app_signature__',
24+
authUrl : 'https://auth.exacttargetapis.com/v1/requestToken?legacy=1'
25+
};
26+
27+
// Simple custom middleware
28+
function tokenFromJWT( req, res, next ) {
29+
// Setup the signature for decoding the JWT
30+
var jwt = new JWT({appSignature: APIKeys.appSignature});
31+
32+
// Object representing the data in the JWT
33+
var jwtData = jwt.decode( req );
34+
35+
// Bolt the data we need to make this call onto the session.
36+
// Since the UI for this app is only used as a management console,
37+
// we can get away with this. Otherwise, you should use a
38+
// persistent storage system and manage tokens properly with
39+
// node-fuel
40+
req.session.token = jwtData.token;
41+
next();
42+
}
43+
44+
/**
45+
THIS IS A WORKAROUND FOR A KNOWN BUG, DO NOT USE THIS CODE IN PRODUCTION
46+
**/
47+
function workaround( req, res, next ) {
48+
if( 'POST' !== req.method ) {
49+
next();
50+
}
51+
52+
if( '/login' === req.url || '/fireEvent/helloWorld' === req.url ) {
53+
next();
54+
}
55+
56+
if(
57+
'/ixn/activities/hello-world/save/' === req.url ||
58+
'/ixn/activities/hello-world/execute/' === req.url ||
59+
'/ixn/activities/hello-world/publish/' === req.url ||
60+
'/ixn/activities/hello-world/validate/' === req.url
61+
){
62+
var buf = '';
63+
req.on('data', function(chunk){
64+
buf += chunk;
65+
});
66+
67+
req.on('end', function(){
68+
try{
69+
var faultyJSON = /"", *}/;
70+
// Cleanup Jira: JB-5249
71+
if( buf.match( faultyJSON ) ) {
72+
req.body = buf.replace( faultyJSON, '""}' );
73+
}
74+
next();
75+
} catch( err ){
76+
console.error( 'ERROR: ', err );
77+
next( err );
78+
}
79+
next();
80+
});
81+
}
82+
}
83+
84+
// Use the cookie-based session middleware
85+
app.use(express.cookieParser());
86+
87+
// TODO: MaxAge for cookie based on token exp?
88+
app.use(express.cookieSession({secret: "HelloWorld-CookieSecret"}));
89+
90+
// Configure Express
91+
app.set('port', process.env.PORT || 3000);
92+
app.set('views', path.join(__dirname, 'views'));
93+
app.set('view engine', 'jade');
94+
app.use(express.logger('dev'));
95+
app.use(workaround);
96+
app.use(express.json());
97+
app.use(express.urlencoded());
98+
app.use(express.methodOverride());
99+
app.use(express.favicon());
100+
app.use(app.router);
101+
app.use(express.static(path.join(__dirname, 'public')));
102+
103+
// Express in Development Mode
104+
if ('development' == app.get('env')) {
105+
app.use(express.errorHandler());
106+
}
107+
108+
// HubExchange Routes
109+
app.get('/', routes.index );
110+
app.post('/login', tokenFromJWT, routes.login );
111+
app.post('/logout', routes.logout );
112+
113+
// Custom Hello World Activity Routes
114+
app.post('/ixn/activities/hello-world/save/', activity.save );
115+
app.post('/ixn/activities/hello-world/validate/', activity.validate );
116+
app.post('/ixn/activities/hello-world/publish/', activity.publish );
117+
app.post('/ixn/activities/hello-world/execute/', activity.execute );
118+
119+
// Custom Hello World Trigger Route
120+
app.post('/ixn/triggers/hello-world/', trigger.edit );
121+
122+
// Abstract Event Handler
123+
app.post('/fireEvent/:type', function( req, res ) {
124+
var data = req.body;
125+
var triggerIdFromAppExtensionInAppCenter = 'helloWorldNTO';
126+
var JB_EVENT_API = 'https://www.exacttargetapis.com/interaction-experimental/v1/events';
127+
var reqOpts = {};
128+
129+
if( 'helloWorld' !== req.params.type ) {
130+
res.send( 400, 'Unknown route param: "' + req.params.type +'"' );
131+
} else {
132+
// Hydrate the request
133+
reqOpts = {
134+
url: JB_EVENT_API,
135+
method: 'POST',
136+
headers: {
137+
'Authorization': 'Bearer ' + req.session.token
138+
},
139+
body: JSON.stringify({
140+
ContactKey: data.alternativeEmail,
141+
EventDefinitionKey: triggerIdFromAppExtensionInAppCenter,
142+
Data: data
143+
})
144+
};
145+
146+
request( reqOpts, function( error, response, body ) {
147+
if( error ) {
148+
console.error( 'ERROR: ', error );
149+
res.send( response, 400, error );
150+
} else {
151+
res.send( body, 200, response);
152+
}
153+
}.bind( this ) );
154+
}
155+
});
156+
157+
app.get('/clearList', function( req, res ) {
158+
// The client makes this request to get the data
159+
activity.logExecuteData = [];
160+
res.send( 200 );
161+
});
162+
163+
164+
// WORKAROUND FOR ACTIVITY COMMA BUG
165+
app.get('/getActivityData', function( req, res ) {
166+
// The client makes this request to get the data
167+
if( !activity.logExecuteData.length ) {
168+
res.send( 200, {data: null} );
169+
} else {
170+
res.send( 200, {data: activity.logExecuteData} );
171+
}
172+
});
173+
174+
http.createServer(app).listen(app.get('port'), function(){
175+
console.log('Express server listening on port ' + app.get('port'));
176+
});

lib/jwtDecoder.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"use strict";
2+
3+
var jwtLib = require('jwt-simple');
4+
var packageJson = require('../package.json');
5+
6+
var VERSION = packageJson.version;
7+
8+
var JwtDecoder = module.exports = function JwtDecoder( options ) {
9+
this.options = options || {};
10+
};
11+
12+
JwtDecoder.VERSION = VERSION;
13+
14+
JwtDecoder.prototype.decode = function( req ) {
15+
var jwtObj = {};
16+
var jwt = req.body.jwt;
17+
18+
try {
19+
var decoded = jwtLib.decode( jwt, this.options.appSignature );
20+
jwtObj.full = decoded;
21+
jwtObj.token = decoded.request.user.oauthToken;
22+
jwtObj.refreshToken = decoded.request.user.refreshToken;
23+
jwtObj.casToken = decoded.request.user.internalOauthToken;
24+
jwtObj.culture = decoded.request.user.culture;
25+
jwtObj.timezone = decoded.request.user.timezone; //OBJECT
26+
jwtObj.expires = ( decoded.request.user.expiresIn * 1000 ) - 60000;
27+
} catch( ex ) {
28+
console.error( 'Decoding failed for jwt: ' + jwt );
29+
console.error( 'Exception: ' + ex );
30+
}
31+
32+
return jwtObj;
33+
};

newrelic.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* New Relic agent configuration.
3+
*
4+
* See lib/config.defaults.js in the agent distribution for a more complete
5+
* description of configuration variables and their potential values.
6+
*/
7+
exports.config = {
8+
/**
9+
* Array of application names.
10+
*/
11+
app_name : ['__insert_name_of_your_app__'],
12+
/**
13+
* Your New Relic license key.
14+
*/
15+
license_key : '__insert_newrelic_key_here__',
16+
logging : {
17+
/**
18+
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
19+
* issues with the agent, 'info' and higher will impose the least overhead on
20+
* production applications.
21+
*/
22+
level : 'info'
23+
}
24+
};

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "helloWorldInteractionExample",
3+
"version": "0.0.3",
4+
"private": true,
5+
"scripts": {
6+
"start": "node app.js"
7+
},
8+
"dependencies": {
9+
"express": "3.4.8",
10+
"jade": "*",
11+
"fuel": "~0.4.1",
12+
"require": "~0.5.0",
13+
"jwt-simple": "~0.1.0",
14+
"newrelic": "~1.3.1",
15+
"request": "~2.33.0"
16+
},
17+
"engines": {
18+
"node": "0.10.7"
19+
}
20+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
define([], function(){
2+
return {
3+
"icon": "images/jb-icon.jpg",
4+
"iconSmall": "images/jb-icon.jpg",
5+
"key": "insert_your_custom_app_extension_activity_name",
6+
"partnerApiObjectTypeId": "IXN.CustomActivity.REST",
7+
"lang": {
8+
"en-US": {
9+
"name": "Hello World",
10+
"description": "Activity simply posts the data into an array for display on the App's home page."
11+
}
12+
},
13+
"category": "messaging",
14+
"version": "1.0",
15+
"apiVersion": "1.0",
16+
"execute": {
17+
"uri": "https://__insert_your_custom_activity_endpoint__/ixn/activities/hello-world/execute/",
18+
"inArguments": [],
19+
"outArguments": [],
20+
"verb": "POST",
21+
"body": "",
22+
"format": "json",
23+
"useJwt": false,
24+
"timeout": 3000
25+
},
26+
"save": {
27+
"uri": "https://__insert_your_custom_activity_endpoint__/ixn/activities/hello-world/save/",
28+
"verb": "POST",
29+
"body": "",
30+
"format": "json",
31+
"useJwt": false,
32+
"timeout": 3000
33+
},
34+
"publish": {
35+
"uri": "https://__insert_your_custom_activity_endpoint__/ixn/activities/hello-world/publish/",
36+
"verb": "POST",
37+
"body": "",
38+
"format": "json",
39+
"useJwt": false,
40+
"timeout": 3000
41+
},
42+
"validate": {
43+
"uri": "https://__insert_your_custom_activity_endpoint__/ixn/activities/hello-world/validate/",
44+
"verb": "POST",
45+
"body": "",
46+
"format": "json",
47+
"useJwt": false,
48+
"timeout": 3000
49+
},
50+
51+
"edit": {
52+
"uri": "https://__insert_your_custom_activity_endpoint__/ixn/activities/hello-world/",
53+
"height": 400,
54+
"width": 500
55+
}
56+
};
57+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
body {
2+
margin: 0px;
3+
padding: 0px;
4+
}
5+
6+
#container {
7+
margin: 30px;
8+
}
6.8 KB
Loading
1.62 KB
Loading

0 commit comments

Comments
 (0)