diff --git a/src/routes/index.js b/src/routes/index.js index 605b9699..96427c0f 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -310,6 +310,10 @@ router.route('/v5/projects/metadata/planConfig/:key/versions/:version(\\d+)') .patch(require('./planConfig/version/update')) .delete(require('./planConfig/version/delete')); +// user level reports +router.route('/v5/projects/reports/embed') +.get(require('./userReports/getEmbedReport')); + // work streams router.route('/v5/projects/:projectId(\\d+)/workstreams') .get(require('./workStreams/list')) diff --git a/src/routes/projectReports/getEmbedReport.js b/src/routes/projectReports/getEmbedReport.js index dc19770c..e43dd7ce 100644 --- a/src/routes/projectReports/getEmbedReport.js +++ b/src/routes/projectReports/getEmbedReport.js @@ -20,7 +20,8 @@ module.exports = [ let REPORTS = null; let allowedUsers = null; try { - allowedUsers = JSON.parse(_.get(config, 'lookerConfig.ALLOWED_USERS', '[]')); + allowedUsers = config.get('lookerConfig.ALLOWED_USERS'); + allowedUsers = allowedUsers ? JSON.parse(allowedUsers) : []; req.log.trace(allowedUsers, 'allowedUsers'); REPORTS = JSON.parse(config.get('lookerConfig.EMBED_REPORTS_MAPPING')); } catch (error) { @@ -105,7 +106,7 @@ module.exports = [ const embedUrl = REPORTS[reportName]; req.log.trace(`Generating embed URL for ${reportName} report, using ${embedUrl} as embed URL.`); if (embedUrl) { - result = await lookerSerivce.generateEmbedUrl(req.authUser, project, member, embedUrl); + result = await lookerSerivce.generateEmbedUrlForProject(req.authUser, project, member, embedUrl); } else { return res.status(404).send('Report not found'); } diff --git a/src/routes/projectReports/getEmbedReport.spec.js b/src/routes/projectReports/getEmbedReport.spec.js index c0d4f174..b6bd0d4c 100644 --- a/src/routes/projectReports/getEmbedReport.spec.js +++ b/src/routes/projectReports/getEmbedReport.spec.js @@ -164,7 +164,7 @@ describe('GET embed report', () => { it('should return 404 when report name not mock and not in EMBED_REPORTS_MAPPING', (done) => { const cfg = sinon.stub(config, 'get'); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); request(server) .get(`/v5/projects/${project1.id}/reports/embed?reportName=random`) .set({ @@ -176,10 +176,27 @@ describe('GET embed report', () => { }); }); + it('should return 403 when report name not mock and not in EMBED_REPORTS_MAPPING', (done) => { + const cfg = sinon.stub(config, 'get'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + // allows only admin user + cfg.withArgs('lookerConfig.ALLOWED_USERS').returns(`[${testUtil.userIds.admin}]`); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock": "/embed/looks/2"}'); + request(server) + .get(`/v5/projects/${project1.id}/reports/embed?reportName=random`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(403, (err) => { + cfg.restore(); + done(err); + }); + }); + it('should return 500 when get admin user error', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock-concrete-customer": "/embed/looks/2"}'); request(server) .get(`/v5/projects/${project1.id}/reports/embed?reportName=mock`) @@ -195,8 +212,8 @@ describe('GET embed report', () => { it('should return 404 when the project template or product template is not found', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock-concrete-customer": "/embed/looks/2"}'); request(server) .get(`/v5/projects/${project0.id}/reports/embed?reportName=mock`) @@ -210,10 +227,48 @@ describe('GET embed report', () => { }); }); + it('should return mock url', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); + const getUser = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.member, + })); + cfg.withArgs('lookerConfig.USE_MOCK').returns('true'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING') + .returns('{"mock": "/customer/embed/looks/2"}'); + request(server) + .get(`/v5/projects/${project1.id}/reports/embed?reportName=mock`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + getUser.restore(); + gem.restore(); + cfg.restore(); + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.equal('generatedUrl'); + const [user, project, member, embedUrl] = gem.lastCall.args; + user.userId.should.equal(testUtil.userIds.member); + project.should.deep.equal({ id: project1.id }); + member.userId.should.equal(testUtil.userIds.member); + embedUrl.should.equal('/customer/embed/looks/2'); + done(); + } + }); + }); + it('should return customer url', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING') .returns('{"mock-concrete-customer": "/customer/embed/looks/2"}'); request(server) @@ -233,9 +288,9 @@ describe('GET embed report', () => { should.exist(resJson); resJson.should.equal('generatedUrl'); const [user, project, member, embedUrl] = gem.lastCall.args; - user.userId.should.equal(40051331); + user.userId.should.equal(testUtil.userIds.member); project.should.deep.equal({ id: project1.id }); - member.userId.should.equal(40051331); + member.userId.should.equal(testUtil.userIds.member); member.role.should.equal('customer'); embedUrl.should.equal('/customer/embed/looks/2'); done(); @@ -245,13 +300,13 @@ describe('GET embed report', () => { it('should return admin url', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); const getAdmin = sinon.stub(util, 'getTopcoderUser', () => ({ firstName: 'fn', lastName: 'ln', userId: 40051333, })); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock-concrete-topcoder": "/admin/embed/looks/2"}'); request(server) .get(`/v5/projects/${project1.id}/reports/embed?reportName=mock`) @@ -271,9 +326,9 @@ describe('GET embed report', () => { should.exist(resJson); resJson.should.equal('generatedUrl'); const [user, project, member, embedUrl] = gem.lastCall.args; - user.userId.should.equal(40051333); + user.userId.should.equal(testUtil.userIds.admin); project.should.deep.equal({ id: project1.id }); - member.userId.should.equal(40051333); + member.userId.should.equal(testUtil.userIds.admin); member.firstName.should.equal('fn'); member.lastName.should.equal('ln'); member.role.should.equal(''); @@ -285,8 +340,8 @@ describe('GET embed report', () => { it('should return copilot url', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock-concrete-copilot": "/copilot/embed/looks/2"}'); request(server) .get(`/v5/projects/${project1.id}/reports/embed?reportName=mock`) @@ -305,9 +360,9 @@ describe('GET embed report', () => { should.exist(resJson); resJson.should.equal('generatedUrl'); const [user, project, member, embedUrl] = gem.lastCall.args; - user.userId.should.equal(40051332); + user.userId.should.equal(testUtil.userIds.copilot); project.should.deep.equal({ id: project1.id }); - member.userId.should.equal(40051332); + member.userId.should.equal(testUtil.userIds.copilot); member.role.should.equal('copilot'); embedUrl.should.equal('/copilot/embed/looks/2'); done(); @@ -317,13 +372,13 @@ describe('GET embed report', () => { it('should return admin url for project with product template', (done) => { const cfg = sinon.stub(config, 'get'); - const gem = sinon.stub(lookerSerivce, 'generateEmbedUrl', () => 'generatedUrl'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForProject', () => 'generatedUrl'); const getAdmin = sinon.stub(util, 'getTopcoderUser', () => ({ firstName: 'fn', lastName: 'ln', userId: 40051333, })); - cfg.withArgs('lookerConfig.USE_MOCK').returns(false); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING') .returns('{"mock-prodCut-topcoder": "/admin/embed/looks/3"}'); request(server) @@ -344,9 +399,9 @@ describe('GET embed report', () => { should.exist(resJson); resJson.should.equal('generatedUrl'); const [user, project, member, embedUrl] = gem.lastCall.args; - user.userId.should.equal(40051333); + user.userId.should.equal(testUtil.userIds.admin); project.should.deep.equal({ id: project3.id }); - member.userId.should.equal(40051333); + member.userId.should.equal(testUtil.userIds.admin); member.firstName.should.equal('fn'); member.lastName.should.equal('ln'); member.role.should.equal(''); diff --git a/src/routes/userReports/getEmbedReport.js b/src/routes/userReports/getEmbedReport.js new file mode 100644 index 00000000..9ce43906 --- /dev/null +++ b/src/routes/userReports/getEmbedReport.js @@ -0,0 +1,76 @@ +/* eslint-disable no-unused-vars */ +import config from 'config'; +import _ from 'lodash'; +import { middleware as tcMiddleware } from 'tc-core-library-js'; +import util from '../../util'; +import { USER_ROLE, ADMIN_ROLES, MANAGER_ROLES } from '../../constants'; +import lookerSerivce from '../../services/lookerService'; + +const permissions = tcMiddleware.permissions; + + +module.exports = [ + async (req, res, next) => { + const mockReport = config.get('lookerConfig.USE_MOCK') === 'true'; + let reportName = mockReport ? 'mock' : req.query.reportName; + const authUser = req.authUser; + let REPORTS = null; + let allowedUsers = null; + try { + allowedUsers = config.get('lookerConfig.ALLOWED_USERS'); + allowedUsers = allowedUsers ? JSON.parse(allowedUsers) : []; + req.log.trace(allowedUsers, 'allowedUsers'); + REPORTS = JSON.parse(config.get('lookerConfig.EMBED_REPORTS_MAPPING')); + } catch (error) { + req.log.error(error); + req.log.debug('Invalid reports mapping. Should be a valid JSON.'); + } + if (!mockReport && !REPORTS) { + return res.status(404).send('Report not found'); + } + + try { + const isAdmin = util.hasRoles(req, ADMIN_ROLES); + const userDisallowed = allowedUsers.length > 0 && !allowedUsers.includes(authUser.userId); + if (userDisallowed) { + req.log.error(`User whitelisting prevented accessing report ${reportName} to ${authUser.userId}`); + return res.status(403).send('User is not allowed to access the report'); + } + const token = await util.getM2MToken(); + const callerUser = await util.getTopcoderUser(authUser.userId, token, req.log); + req.log.trace(callerUser, 'callerUser'); + const member = { + firstName: callerUser.firstName, + lastName: callerUser.lastName, + userId: authUser.userId, + role: '', + }; + let roleKey = ''; + if (!mockReport) { + if (util.hasRoles(req, [USER_ROLE.COPILOT])) { + roleKey = 'copilot'; + } else if (isAdmin || util.hasRoles(req, MANAGER_ROLES)) { + roleKey = 'topcoder'; + } else { + roleKey = 'customer'; + } + reportName = `${reportName}-${roleKey}`; + } + // pick the report based on its name + let result = {}; + const embedUrl = REPORTS[reportName]; + req.log.trace(`Generating embed URL for ${reportName} report, using ${embedUrl} as embed URL.`); + if (embedUrl) { + result = await lookerSerivce.generateEmbedUrlForUser(req.authUser, member, embedUrl); + } else { + return res.status(404).send('Report not found'); + } + + req.log.trace(result); + return res.status(200).json(result); + } catch (err) { + req.log.error(err); + return res.status(500).send(err.toString()); + } + }, +]; diff --git a/src/routes/userReports/getEmbedReport.spec.js b/src/routes/userReports/getEmbedReport.spec.js new file mode 100644 index 00000000..ca8bf89e --- /dev/null +++ b/src/routes/userReports/getEmbedReport.spec.js @@ -0,0 +1,250 @@ +import chai from 'chai'; +import sinon from 'sinon'; +import request from 'supertest'; +import config from 'config'; +import server from '../../app'; +import testUtil from '../../tests/util'; +import util from '../../util'; +import lookerSerivce from '../../services/lookerService'; + +const should = chai.should(); + +describe('GET embed report', () => { + beforeEach((done) => { + testUtil.clearDb() + .then(() => { + done(); + }); + }); + + after((done) => { + testUtil.clearDb(done); + }); + + describe('GET /project/reports/embed', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + }); + + it('should return 404 when EMBED_REPORTS_MAPPING is not defined', (done) => { + const cfg = sinon.stub(config, 'get'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + request(server) + .get('/v5/projects/reports/embed?reportName=random') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(404, (err) => { + cfg.restore(); + done(err); + }); + }); + + it('should return 404 when report name not mock and not in EMBED_REPORTS_MAPPING', (done) => { + const cfg = sinon.stub(config, 'get'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock": "/embed/looks/2"}'); + const getUser = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.member, + })); + request(server) + .get('/v5/projects/reports/embed?reportName=random') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(404, (err) => { + getUser.restore(); + cfg.restore(); + done(err); + }); + }); + + it('should return 403 when report name not mock and not in EMBED_REPORTS_MAPPING', (done) => { + const cfg = sinon.stub(config, 'get'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + // allows only admin user + cfg.withArgs('lookerConfig.ALLOWED_USERS').returns(`[${testUtil.userIds.admin}]`); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock": "/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=random') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(403, (err) => { + cfg.restore(); + done(err); + }); + }); + + it('should return 500 when get admin user error', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForUser', () => 'generatedUrl'); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"mock-concrete-customer": "/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=mock') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(500, (err) => { + gem.restore(); + cfg.restore(); + done(err); + }); + }); + + it('should return mock url', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForUser', () => 'generatedUrl'); + const getUser = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.member, + })); + cfg.withArgs('lookerConfig.USE_MOCK').returns('true'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING') + .returns('{"mock": "/customer/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=summary') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + getUser.restore(); + gem.restore(); + cfg.restore(); + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.equal('generatedUrl'); + const [user, member, embedUrl] = gem.lastCall.args; + user.userId.should.equal(testUtil.userIds.member); + member.userId.should.equal(testUtil.userIds.member); + embedUrl.should.equal('/customer/embed/looks/2'); + done(); + } + }); + }); + + it('should return customer url', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForUser', () => 'generatedUrl'); + const getUser = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.member, + })); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING') + .returns('{"summary-customer": "/customer/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=summary') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + getUser.restore(); + gem.restore(); + cfg.restore(); + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.equal('generatedUrl'); + const [user, member, embedUrl] = gem.lastCall.args; + user.userId.should.equal(testUtil.userIds.member); + member.userId.should.equal(testUtil.userIds.member); + embedUrl.should.equal('/customer/embed/looks/2'); + done(); + } + }); + }); + + it('should return admin url', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForUser', () => 'generatedUrl'); + const getAdmin = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.admin, + })); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"summary-topcoder": "/admin/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=summary') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + getAdmin.restore(); + gem.restore(); + cfg.restore(); + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.equal('generatedUrl'); + const [user, member, embedUrl] = gem.lastCall.args; + user.userId.should.equal(testUtil.userIds.admin); + member.userId.should.equal(testUtil.userIds.admin); + member.firstName.should.equal('fn'); + member.lastName.should.equal('ln'); + embedUrl.should.equal('/admin/embed/looks/2'); + done(); + } + }); + }); + + it('should return copilot url', (done) => { + const cfg = sinon.stub(config, 'get'); + const gem = sinon.stub(lookerSerivce, 'generateEmbedUrlForUser', () => 'generatedUrl'); + const getUser = sinon.stub(util, 'getTopcoderUser', () => ({ + firstName: 'fn', + lastName: 'ln', + userId: testUtil.userIds.copilot, + })); + cfg.withArgs('lookerConfig.USE_MOCK').returns('false'); + cfg.withArgs('lookerConfig.EMBED_REPORTS_MAPPING').returns('{"summary-copilot": "/copilot/embed/looks/2"}'); + request(server) + .get('/v5/projects/reports/embed?reportName=summary') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + getUser.restore(); + gem.restore(); + cfg.restore(); + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.equal('generatedUrl'); + const [user, member, embedUrl] = gem.lastCall.args; + user.userId.should.equal(testUtil.userIds.copilot); + member.userId.should.equal(testUtil.userIds.copilot); + embedUrl.should.equal('/copilot/embed/looks/2'); + done(); + } + }); + }); + }); +}); diff --git a/src/services/lookerService.js b/src/services/lookerService.js index 0401eb81..e645e0bb 100644 --- a/src/services/lookerService.js +++ b/src/services/lookerService.js @@ -92,6 +92,44 @@ function createdSignedEmbedUrl(options) { return `${host}${embedPath}?${queryString}`; } +/** + * Generates the looker embed URL for the given look/dashboard. + * + * @param {Object} authUser requesting user + * @param {Object} member member object for the requesting user + * @param {String} reportUrl embed URL (look,dashboard etc) + * @returns {String} URL for embedding the looker report, it can be GET only once + */ +function generateEmbedUrlForUser(authUser, member, reportUrl) { + const SESSION_LENGTH = parseInt(config.lookerConfig.SESSION_LENGTH, 10); + const urlData = { + host: config.lookerConfig.LOOKER_HOST, + secret: config.lookerConfig.EMBED_KEY, + external_user_id: authUser.userId, + group_ids: [], + first_name: member.firstName, + last_name: member.lastName, + permissions: ['access_data', 'see_looks', 'see_user_dashboards', 'schedule_look_emails', 'download_with_limit'], + models: ['tc_user_projects'], + access_filters: { + tc_user_projects: { + tc_user_id: `${member.userId}`, + }, + }, + user_attributes: { + user_roles_project: member.role, + user_roles_platform: authUser.roles, + tc_user_id: member.userId, + }, + session_length: SESSION_LENGTH, + embed_url: reportUrl, + force_logout_login: true, + }; + + const url = createdSignedEmbedUrl(urlData); + return `https://${url}`; +} + /** * Generates the looker embed URL for the given look/dashboard. * @@ -101,7 +139,7 @@ function createdSignedEmbedUrl(options) { * @param {String} reportUrl embed URL (look,dashboard etc) * @returns {String} URL for embedding the looker report, it can be GET only once */ -function generateEmbedUrl(authUser, project, member, reportUrl) { +function generateEmbedUrlForProject(authUser, project, member, reportUrl) { const SESSION_LENGTH = parseInt(config.lookerConfig.SESSION_LENGTH, 10); const urlData = { host: config.lookerConfig.LOOKER_HOST, @@ -121,6 +159,7 @@ function generateEmbedUrl(authUser, project, member, reportUrl) { connect_project_id: `${project.id}`, user_roles_project: member.role, user_roles_platform: authUser.roles, + tc_user_id: member.userId, }, session_length: SESSION_LENGTH, embed_url: reportUrl, @@ -132,5 +171,6 @@ function generateEmbedUrl(authUser, project, member, reportUrl) { } module.exports = { - generateEmbedUrl, + generateEmbedUrlForUser, + generateEmbedUrlForProject, }; diff --git a/src/tests/util.js b/src/tests/util.js index 08f0a657..6bbd1217 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -21,7 +21,7 @@ export default { jwts: { // userId = 40051331, [ 'Topcoder User' ],handle: 'test1',email: 'test@topcoder.com' member: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzEiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.pDtRzcGQjgCBD6aLsW-1OFhzmrv5mXhb8YLDWbGAnKo', - // userId = 40051332,roles: [ 'Topcoder copilot' ],handle: 'test1',email: 'test@topcoder.com' + // userId = 40051332,roles: [ 'Connect copilot' ],handle: 'test1',email: 'test@topcoder.com' copilot: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBDb3BpbG90Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjo0MDA1MTMzMiwiZW1haWwiOiJ0ZXN0QHRvcGNvZGVyLmNvbSIsImlhdCI6MTQ3MDYyMDA0NH0.DnX17gBaVF2JTuRai-C2BDSdEjij9da_s4eYcMIjP0c', // userId = 40051333, roles: [ 'administrator', 'Topcoder User' ],handle: 'test1',email: 'test@topcoder.com' admin: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw',