Skip to content

Commit 018da93

Browse files
committed
init gamification
1 parent f04f7fa commit 018da93

File tree

5 files changed

+148
-2
lines changed

5 files changed

+148
-2
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v8.9.2

config/default.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,11 @@ module.exports = {
118118
// Private group ids will be excluded from results for non-admin users.
119119
PRIVATE_GROUP_IDS: JSON.parse(process.env.PRIVATE_GROUP_IDS || '["20000000"]'),
120120
// id of the tcwebservice user, used to audit fields in case of m2m tokens
121-
TC_WEBSERVICE_USERID: process.env.TC_WEBSERVICE_USERID || 22838965
121+
TC_WEBSERVICE_USERID: process.env.TC_WEBSERVICE_USERID || 22838965,
122+
123+
// Gamification
124+
MAMBO_PUBLIC_KEY: process.env.MAMBO_PUBLIC_KEY,
125+
MAMBO_PRIVATE_KEY: process.env.MAMBO_PRIVATE_KEY,
126+
MAMBO_DOMAIN_URL: process.env.MAMBO_DOMAIN_URL,
127+
MAMBO_DEFAULT_SITE: process.env.MAMBO_DEFAULT_SITE
122128
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Controller for Gamification endpoints
3+
*/
4+
const service = require('../services/GamificationService')
5+
6+
/**
7+
* Get member's rewards
8+
* @param {Object} req the request
9+
* @param {Object} res the response
10+
*/
11+
async function getMemberRewards (req, res) {
12+
const result = await service.getMemberRewards(req.params.handle, req.query)
13+
res.send(result)
14+
}
15+
16+
module.exports = {
17+
getMemberRewardsAll
18+
}

src/routes.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,14 @@ module.exports = {
153153
allowNoToken: true,
154154
scopes: [MEMBERS.READ, MEMBERS.ALL]
155155
}
156-
}
156+
},
157+
'/members/:handle/gamification/rewards': {
158+
get: {
159+
controller: 'GamificationController',
160+
method: 'getMemberRewards',
161+
auth: 'jwt',
162+
allowNoToken: true,
163+
scopes: [MEMBERS.READ, MEMBERS.ALL]
164+
}
165+
},
157166
}

src/services/GamificationService.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* This service provides operations of gamification.
3+
*/
4+
5+
const _ = require('lodash')
6+
const Joi = require('joi')
7+
const config = require('config')
8+
const request = require('request')
9+
const helper = require('../common/helper')
10+
const logger = require('../common/logger')
11+
const errors = require('../common/errors')
12+
13+
// The Mambo API uses specifically the OAuth 2.0 Client Credentials grant flow.
14+
// https://api.mambo.io/docs/#authentication
15+
// this is where the token is stored after it is exchanged from Mambo
16+
// it is used on all calls to Mambo API
17+
let MAMBO_ACCESS_TOKEN;
18+
19+
/**
20+
* This function Request Access Token from Mambo by using Client Credentials flow.
21+
* When token is provided it is stored in MAMBO_ACCESS_TOKEN
22+
* @returns Promise
23+
*/
24+
async function getAccessToken() {
25+
const options = {
26+
method: 'POST',
27+
url: `${config.MAMBO_DOMAIN_URL}/oauth/token`,
28+
headers: {
29+
'Authorization': `Basic ${Buffer.from(`${config.MAMBO_PUBLIC_KEY}:${config.MAMBO_PRIVATE_KEY}`).toString('base64')}`,
30+
'Content-Type': 'application/x-www-form-urlencoded'
31+
},
32+
form: {
33+
'grant_type': 'client_credentials'
34+
}
35+
};
36+
// wrap in Promise and return
37+
return new Promise(function (resolve, reject) {
38+
request(options,
39+
function (error, response, body) {
40+
if (response.statusCode === 200) {
41+
MAMBO_ACCESS_TOKEN = JSON.parse(body)
42+
resolve(MAMBO_ACCESS_TOKEN)
43+
} else {
44+
reject(error)
45+
}
46+
}
47+
)
48+
})
49+
}
50+
51+
52+
/**
53+
* Get a user's rewards and all available rewards
54+
* API details https://api.mambo.io/docs/#!/Users/getUserRewards
55+
* @param {String} handle the member handle
56+
* @param {Object} query the query parameters
57+
* @returns {Object} the rewards
58+
*/
59+
async function getMemberRewards(handle, query) {
60+
// if no API token, try get one first
61+
if (!MAMBO_ACCESS_TOKEN) await getAccessToken();
62+
// prepare the API request
63+
let apiQuery = '?';
64+
if (query.tags) {
65+
const tags = helper.parseCommaSeparatedString(query.tyags)
66+
apiQuery += tags.map(t => `tags=${t}`).join('&')
67+
}
68+
if (query.tagsJoin && query.tags) {
69+
apiQuery += `&tagsJoin=${query.tagsJoin}`
70+
}
71+
const options = {
72+
method: 'GET',
73+
url: `${config.MAMBO_DOMAIN_URL}/api/v1/${query.site || config.MAMBO_DEFAULT_SITE}/users/${encodeURIComponent(handle)}/rewards${apiQuery}`,
74+
headers: {
75+
'Authorization': `Bearer ${MAMBO_ACCESS_TOKEN.access_token}`
76+
}
77+
};
78+
// wrap in Promise and return
79+
return new Promise(function (resolve, reject) {
80+
request(options,
81+
function (error, response, body) {
82+
if (response.statusCode === 200) {
83+
resolve(JSON.parse(body))
84+
} else {
85+
if (response.statusCode === 401) {
86+
// token expired
87+
// reset it and call the function to retry
88+
MAMBO_ACCESS_TOKEN = null;
89+
resolve(getMemberRewards(handle, query))
90+
} else {
91+
reject(error)
92+
}
93+
}
94+
}
95+
)
96+
})
97+
}
98+
99+
getMemberRewards.schema = {
100+
handle: Joi.string().required(),
101+
query: Joi.object().keys({
102+
site: Joi.string(),
103+
tags: Joi.string(),
104+
tagsJoin: Joi.string().valid('hasAllOf', 'hasAnyOf')
105+
})
106+
}
107+
108+
module.exports = {
109+
getMemberRewards
110+
}
111+
112+
logger.buildService(module.exports)

0 commit comments

Comments
 (0)