diff --git a/api/.babelrc b/api/.babelrc index 1320b9a32..81bfbad3f 100644 --- a/api/.babelrc +++ b/api/.babelrc @@ -1,3 +1,7 @@ { - "presets": ["@babel/preset-env"] + "presets": ["@babel/preset-env"], + "plugins": [ + "@babel/plugin-proposal-throw-expressions", + ["@babel/plugin-proposal-class-properties", { "loose": true }] + ] } diff --git a/api/package-lock.json b/api/package-lock.json index d5e74e85c..8292250ea 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -575,6 +575,20 @@ "@babel/plugin-syntax-async-generators": "^7.0.0" } }, + "@babel/plugin-proposal-class-properties": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.1.0.tgz", + "integrity": "sha512-/PCJWN+CKt5v1xcGn4vnuu13QDoV+P7NcICP44BoonAJoPSGwVkgrXihFIQGiEjjPlUDBIw1cM7wYFLARS2/hw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0", + "@babel/plugin-syntax-class-properties": "^7.0.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz", @@ -602,6 +616,16 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.0.0" } }, + "@babel/plugin-proposal-throw-expressions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.0.0.tgz", + "integrity": "sha512-CA2EUiwnbXrsdV4hy3jYghm91WaL7zL7xYfu628dyItRr6gylbRxshghGEK/Hhm//rR58N3PBmEeuYqSW57IUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-throw-expressions": "^7.0.0" + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz", @@ -620,6 +644,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-class-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz", + "integrity": "sha512-cR12g0Qzn4sgkjrbrzWy2GE7m9vMl/sFkqZ3gIpAQdrvPDnLM8180i+ANDFIXfjHo9aqp0ccJlQ0QNZcFUbf9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz", @@ -644,6 +677,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-throw-expressions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.0.0.tgz", + "integrity": "sha512-/5uORdWlPta/ALhI5zKtm0Y9vAYOa7HJMML0OnCGk9XZA4hpGjb0Xjt/OVDCJVawC/4FrlAGCHOaj9BtWeVDvg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz", diff --git a/api/package.json b/api/package.json index 057991f3d..f50720958 100644 --- a/api/package.json +++ b/api/package.json @@ -37,7 +37,10 @@ "winston": "^3.1.0" }, "devDependencies": { + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-throw-expressions": "^7.0.0", "graphql-cli": "^2.16.5", + "lodash.merge": "^4.6.1", "npm-run-all": "4.1.3", "prisma": "^1.18.1" } diff --git a/api/src/auth/middleware/getMember.js b/api/src/auth/middleware/getMember.js deleted file mode 100644 index 7d19b2d6c..000000000 --- a/api/src/auth/middleware/getMember.js +++ /dev/null @@ -1,10 +0,0 @@ -const getMember = async (req, res, next, db) => { - if (!req.user) return next() - const member = await db.query.member({ - where: { authId: req.user.sub } - }) - req.user = { user: member } - next() -} - -module.exports = { getMember } diff --git a/api/src/auth/validateIdToken.js b/api/src/auth/validateIdToken.js deleted file mode 100644 index dd0b794b3..000000000 --- a/api/src/auth/validateIdToken.js +++ /dev/null @@ -1,28 +0,0 @@ -const jwksClient = require('jwks-rsa') -const jwt = require('jsonwebtoken') -const jwks = jwksClient({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 1, - jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json` -}) - -const validateIdToken = idToken => - new Promise((resolve, reject) => { - const { header, payload } = jwt.decode(idToken, { complete: true }) - if (!header || !header.kid || !payload) reject(new Error('Invalid Token')) - jwks.getSigningKey(header.kid, (err, key) => { - if (err) reject({ error: `Error getting signing key: ${err.message}` }) - jwt.verify( - idToken, - key.publicKey, - { algorithms: ['RS256'] }, - (err, decoded) => { - if (err) reject('jwt verify error: ' + err.message) - resolve(decoded) - } - ) - }) - }) - -module.exports = { validateIdToken } diff --git a/api/src/directives/index.js b/api/src/directives/index.js index 8e6b925ae..be47f90c1 100644 --- a/api/src/directives/index.js +++ b/api/src/directives/index.js @@ -1,13 +1,12 @@ import { SchemaDirectiveVisitor } from 'apollo-server-express' import _get from 'lodash.get' -import { logger } from '../logger' - -import { AuthenticationError, ForbiddenError } from '../errors' -import { validateIdToken } from '../auth/validateIdToken' +import { logger, AuthenticationError } from '../logger' const memberLocationOnContext = 'request.user' +const userIdOnContext = 'request.headers.userid' const bearerAccessToken = 'request.headers.authorization' +const ctxUserId = ctx => _get(ctx, userIdOnContext) const ctxMember = ctx => _get(ctx, memberLocationOnContext) const ctxToken = ctx => _get(ctx, bearerAccessToken) @@ -29,48 +28,20 @@ const createMember = async (ctx, idToken, info) => { return member } -const isLoggedIn = async ctx => { - logger.info(`isLoggedIn 0: ${JSON.stringify(ctx.request.headers)}`) - let member = ctxMember(ctx, memberLocationOnContext) - let token = ctxToken(ctx, bearerAccessToken) - let memberToken - logger.info('isLoggedIn: ', member, token) - if (!member && token) { - let scheme, credentials - const tokenParts = token.split(' ') - if (tokenParts.length === 2) { - scheme = tokenParts[0] - credentials = tokenParts[1] - } - - if (/^Bearer$/i.test(scheme)) { - token = credentials - //verify token - try { - memberToken = await validateIdToken(token) - const authId = memberToken.sub - exists = await ctx.db.query.member({ where: { authId: authId } }) - if (!member && exists === null) { - member = await createMember(ctx, memberToken).then(res => { - return res - }) - - return member - } - return member - } catch (err) { - memberToken = false - } - } - } - if (!member) { - throw new AuthenticationError('Access token is missing or expired') - } +const isUserId = async ctx => { + logger.info(`isUserId 0: ${ctx.request.headers.userid}`) + logger.info(`isUserId 1: ${ctx.request}`) + let member = await ctxUserId(ctx) + logger.info(member) + if (!member) { + throw new AuthenticationError() + } else { return member + } } -const isRequestingMemberAlsoOwner = ({ ctx, memberId, type, typeId }) => - ctx.db.exists[type]({ slug: typeId, owner: memberId }) +const isRequestingMemberAlsoOwner = ({ ctx, memberId, type, teamSlug }) => + ctx.db.exists[type]({ slug: teamSlug, owner: memberId }) const isRequestingMember = ({ ctx, memberId }) => ctx.db.exists.Member({ authId: memberId }) @@ -89,17 +60,18 @@ const assertAuth = ctx => { // Directive resolvers (apollo v1 syntax) export const directiveResolvers = { isAuthenticated: async (next, source, args, ctx) => { - let result = await isLoggedIn(ctx) - logger.info('directive isAuthenticated: ', result) + let result = await isUserId(ctx) + logger.info('directive isAuthenticated: ') + logger.info(await result) return next(result) }, hasRole: (next, source, { roles }, ctx) => { - const { role } = isLoggedIn(ctx) + const { role } = isUserId(ctx) logger.info('directive hasRole: ', role) if (roles.includes(role)) { return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') }, isOwner: async (next, source, { type }, ctx) => { logger.info('directive isOwner: ', source, type, ctx) @@ -107,47 +79,46 @@ export const directiveResolvers = { source && source.id ? source : ctx.request.body.variables ? ctx.request.body.variables : { id: null } - const user = await isLoggedIn(ctx) - const memberId = user.sub + const userId = await isUserId(ctx) const isOwner = type === `Member` ? memberId === typeId - : await isRequestingMemberAlsoOwner({ ctx, memberId, type, typeId }) + : await isRequestingMemberAlsoOwner({ ctx, userId, type, typeId }) if (isOwner) { return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') }, isOwnerOrHasRole: async (next, source, { roles, type }, ctx, ...p) => { - const { authId: memberId } = await isLoggedIn(ctx) - logger.info('directive isOwnerOrHasRole 1: ', memberId, roles, type) - if(memberId === undefined && role=== undefined) throw new Error(`Not logged in`) + const userId = await isUserId(ctx) + logger.info('directive isOwnerOrHasRole 1: ', userId, roles, type) + if(userId === undefined && role=== undefined) throw new Error(`Not logged in`) if (roles.includes(role)) { return next() } - const { authId: typeId } = ctx.request.body.variables + const { teamSlug } = ctx.request.body.variables const isOwner = await isRequestingMemberAlsoOwner({ ctx, - memberId, + userId, type, - typeId + teamSlug }) - logger.info('directive isOwnerOrHasRole 2: ', typeId, memberId, role, isOwner) + logger.info('directive isOwnerOrHasRole 2: ', teamSlug, userId, role, isOwner) if (isOwner) { return next() } const hasRole = await isRequestingMember({ ctx, - memberId, + userId, type, - typeId + teamSlug }) if(hasRole){ return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') } } diff --git a/api/src/graphql/TeamFBFragment.js b/api/src/graphql/fragments/TeamFBFragment.js similarity index 90% rename from api/src/graphql/TeamFBFragment.js rename to api/src/graphql/fragments/TeamFBFragment.js index f7030921d..b0f99c664 100644 --- a/api/src/graphql/TeamFBFragment.js +++ b/api/src/graphql/fragments/TeamFBFragment.js @@ -1,4 +1,4 @@ -const gql = require('graphql-tag') +import gql from 'graphql-tag' const TEAM_FB_FRAGMENT = gql`{ id diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/fragments/TeamsConnectionsFragment.js similarity index 95% rename from api/src/graphql/TeamsConnectionsFragment.js rename to api/src/graphql/fragments/TeamsConnectionsFragment.js index 5193475e0..f019dd02e 100644 --- a/api/src/graphql/TeamsConnectionsFragment.js +++ b/api/src/graphql/fragments/TeamsConnectionsFragment.js @@ -1,4 +1,4 @@ -const gql = require('graphql-tag') +import gql from 'graphql-tag' const TEAMS_CONNECTIONS_FRAGMENT = gql`{ edges { diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/fragments/TeamsFragment.js similarity index 93% rename from api/src/graphql/TeamsFragment.js rename to api/src/graphql/fragments/TeamsFragment.js index 442829e3c..1dd527af9 100644 --- a/api/src/graphql/TeamsFragment.js +++ b/api/src/graphql/fragments/TeamsFragment.js @@ -1,4 +1,4 @@ -const gql = require('graphql-tag') +import gql from 'graphql-tag' const TEAMS_FRAGMENT = gql`{ id diff --git a/api/src/graphql/resolvers/Mutation/Member.js b/api/src/graphql/resolvers/Mutation/Member.js new file mode 100644 index 000000000..653764d4c --- /dev/null +++ b/api/src/graphql/resolvers/Mutation/Member.js @@ -0,0 +1,17 @@ +export const createMember = async function (ctx, info, authId) { + logger.info('createMember', authId) + const member = await ctx.db.mutation.upsertMember({ + where: { + authId: authId, + }, + create: { + authId: authId, + alias: idToken.nickname + }, + update: { + authId: authId, + alias: idToken.nickname + } + }, info) + return member +} diff --git a/api/src/graphql/resolvers/Mutation/Team.js b/api/src/graphql/resolvers/Mutation/Team.js new file mode 100644 index 000000000..e69de29bb diff --git a/api/src/graphql/resolvers/Mutation/TeamMember.js b/api/src/graphql/resolvers/Mutation/TeamMember.js new file mode 100644 index 000000000..e69de29bb diff --git a/api/src/graphql/resolvers/Mutation/index.js b/api/src/graphql/resolvers/Mutation/index.js new file mode 100644 index 000000000..a596e0100 --- /dev/null +++ b/api/src/graphql/resolvers/Mutation/index.js @@ -0,0 +1,194 @@ +import axios from 'axios' + +import { getUser } from '../../../middleware/getMember' + +import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from '../../../email/sendgrid' + +import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment' +import TEAMS_FRAGMENT from '../../fragments/TeamsFragment' +import TEAM_FB_FRAGMENT from '../../fragments/TeamFBFragment' + +import { logger, AuthenticationError } from '../../../logger' + +export const resolvers = { + Mutation: { + async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { + const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) + const addMember = await ctx.db.mutation.upsertTeamMembers({ + where:{email: email}, + create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}}, + update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}}, + `{ id }`) + const sendInvite = await sendTeamMemberInvite(email, team) + return sendInvite + }, + async verifyTeamInvite(parent, { token }, ctx, info) { + let verifiedToken = null + let team = null + try { + verifiedToken = await verifyInviteToken(token) + team = { + email: verifiedToken.email, + team: { + slug: verifiedToken.team + } + } + logger.info('verifyTeamInvite.token: ', await verifiedToken) + } catch (err) { + logger.debug('verifyTeamInvite. err: ', err) + throw new Error(err.message) + } + return team + }, + async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { + logger.info('createTeam ctx.request.user:') + logger.info(ctx.request.user) + + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + + return getUser(authId) + .then(async result => { + logger.info('axios:') + logger.info(result) + + const alias = result.data.users_User.alias + const email = result.data.users_User.email + const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png' + const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png' + + const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`) + logger.info('createTeamUrl:') + logger.info(JSON.stringify(await createTeamUrl)) + + const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/newTeam/' + // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' + + logger.info(`${platformUrl}${createTeamUrl}/${authId}`) + const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) + .then((result) => { + console.log('createPlatformTeam result:', result.data) + if(result.data.message === 'Team Name already taken'){ + return result.data.message + } + if(result.data.result === 'Fail'){ + return result.data.message + } + return result + }) + .catch(err =>{ + logger.debug('createPlatformTeam err:') + logger.debug(err) + throw new Error(err) + }) + console.log('createPlatformTeam returned: ', await createPlatformTeam) + if(await createPlatformTeam === 'Team Name already taken'){ + return createPlatformTeam + } + + const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) + .catch((err) => { + logger.debug(err, 'existingMember error: ') + return err + }) + + logger.info('existingMember:') + logger.info(await existingMember) + + let createTeam + if(existingMember.id !== null){ + logger.info('createTeam with existingMember:') + createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) + .catch((err) => { + logger.debug(err, 'createTeamMutation error: ') + return err + }) + } else { + logger.info('createTeam without existingMember:') + createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) + .catch((err) => { + logger.debug(err, 'createTeamMutation error: ') + return err + }) + } + + // sendTeamCreateConfirmation(email, name, botName) + + return createTeam + }) + .catch((err) => { + logger.debug('createTeam error: ') + return err + }) + }, + async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { + return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) + .catch((err) => { + logger.debug('createTeam error: ', err) + return err + }) + }, + async updateFB(parent, { fbId, avatar}, ctx, info) { + return ctx.db.mutation.updateFinancialBeings({data:{avatar:avatar}, where:{id:fbId}}, info) + .catch((err) => { + logger.debug('createTeam error: ', err) + return err + }) + }, + async deleteTeam(parent, { slug, botSlug }, ctx, info) { + logger.info('DeleteTeam resolver') + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + logger.info(authId) + logger.info('getUser') + logger.info(getUser) + + return getUser(authId) + .then(async result => { + logger.info('deleteTeam axios:') + logger.info(await result) + + const alias = await result.data.users_User.alias + // const email = result.data.users_User.email + const deleteTeamUrl = encodeURI(`${slug}/${alias}/${botSlug}`) + logger.info('deleteTeamUrl:') + logger.info(JSON.stringify(await deleteTeamUrl)) + + const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/deleteTeam/' + // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' + + logger.info(`${platformUrl}${deleteTeamUrl}/${authId}`) + const deletePlatformTeam = await axios.get(`${platformUrl}${deleteTeamUrl}/${authId}`) + .then((result) => { + console.log('deletePlatformTeam result:', result.data) + if(result.data.result === 'Fail'){ + return result.data.message + } + return result + }) + .catch(err =>{ + logger.debug('deletePlatformTeam err:') + logger.debug(err) + throw new Error(err) + }) + console.log('deletePlatformTeam returned: ', await deletePlatformTeam) + + return ctx.db.mutation.deleteTeam({ where: { slug } }, info) + .catch((res) => { + logger.info('deleteTeam error: ', res) + const errors = res.graphQLErrors.map((error) => { + return error.message + }) + }) + }) + .catch((err) => { + logger.debug('deleteTeam error: ') + return err + }) + } + } +} diff --git a/api/src/graphql/resolvers/Query/Member.js b/api/src/graphql/resolvers/Query/Member.js new file mode 100644 index 000000000..2ca9d69d6 --- /dev/null +++ b/api/src/graphql/resolvers/Query/Member.js @@ -0,0 +1,30 @@ +import { logger, AuthenticationError, DatabaseError } from '../../../logger' + +export const member = async (parent, arg, ctx, info) => { + logger.info(' --> resolver.member') + logger.info(ctx.request) + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + return ctx.db.query.member({ where: { authId } }, info) + .catch(err => { + logger.debug('resolver.member error: ') + logger.debug(err) + throw new DatabaseError(`Member Data Error: ${err.message}`) + }) +} + +export const owner = async (parent, args, ctx, info) => { + logger.info('resolver.query.owner ctx: ') + const authId = ctx.request.userId + if (!authId) { + throw new AuthenticationError() + } + return ctx.db.query.member({ where: { authId: authId } }, info) + .catch(err => { + logger.debug('resolver.owner error: ') + logger.debug(err) + throw new DatabaseError(`Member Data Error: ${err.message}`) + }) +} diff --git a/api/src/graphql/resolvers/Query/Team.js b/api/src/graphql/resolvers/Query/Team.js new file mode 100644 index 000000000..4961888e5 --- /dev/null +++ b/api/src/graphql/resolvers/Query/Team.js @@ -0,0 +1,51 @@ +import { logger } from '../../../logger' + +import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment' +import TEAMS_FRAGMENT from '../../fragments/TeamsFragment' + +export const teams = async (parent, args, ctx, info) => { + logger.info('teams') + console.log('teams') + return ctx.db.query.teamsConnection({}, TEAMS_CONNECTIONS_FRAGMENT) +} + +export const teamById = async (parent, { id }, ctx, info) => { + return ctx.db.query.team({ where: { id } }, info) +} + +export const teamByName = async (parent, { name }, ctx, info) => { + return ctx.db.query.team({ where: { name: name } }, `{ name }`) +} + +export const teamWithRole = async (parent, { teamId, role }, ctx, info) => { + return ctx.db.query.teamsConnection({where: {AND: [{id: teamId},{members_some:{role: role}}]}, first:1}, info) +} + +export const teamsByOwner = async (parent, args, ctx, info) => { + logger.info('teamsByOwner') + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + return ctx.db.query.teams({where: { owner: authId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) +} + +export const teamsByRole = async (parent, args, ctx, info) => { + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + let teamAdmin + try { + teamAdmin= await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}, role_not:'MEMBER'}]}}}, TEAMS_FRAGMENT) + } catch (err) { + logger.debug('teamsByRole error: ') + logger.debug(err) + const errors = res.graphQLErrors.map((error) => { + return error.message + }) + return errors + } + logger.info('teamsByRole teamAdmin: ', teamAdmin) + return teamAdmin +} diff --git a/api/src/graphql/resolvers/Query/TeamMember.js b/api/src/graphql/resolvers/Query/TeamMember.js new file mode 100644 index 000000000..9126c9c5d --- /dev/null +++ b/api/src/graphql/resolvers/Query/TeamMember.js @@ -0,0 +1,12 @@ +import { logger } from '../../../logger' + +export const fbByTeamMember = async (parent, args, ctx, info) => { + logger.info('fbByTeamMember: ', args, ctx.request, ctx.request.res.req.user) + const authId = ctx.request.headers.userid + if (!authId) { + throw new AuthenticationError() + } + let teamMemberFB = await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}}]}}, orderBy: 'updatedAt_DESC'}, TEAM_FB_FRAGMENT) + logger.info('fbByTeamMember response: ', await teamMemberFB[0]) + return teamMemberFB[0] +} diff --git a/api/src/graphql/resolvers/Query/index.js b/api/src/graphql/resolvers/Query/index.js new file mode 100644 index 000000000..5616f8cbb --- /dev/null +++ b/api/src/graphql/resolvers/Query/index.js @@ -0,0 +1,19 @@ +import { logger } from '../../../logger' + +import { member, owner } from './Member' +import { teams, teamById, teamByName, teamWithRole, teamsByOwner, teamsByRole } from './Team' +import{ fbByTeamMember } from './TeamMember' + +export const resolvers = { + Query: { + member: (parent, arg, ctx, info) => member(parent, arg, ctx, info), + teams: (parent, arg, ctx, info) => teams(parent, arg, ctx, info), + teamById: (parent, arg, ctx, info) => teamById(parent, arg, ctx, info), + teamByName: (parent, arg, ctx, info) => teamByName(parent, arg, ctx, info), + teamWithRole: (parent, arg, ctx, info) => teamWithRole(parent, arg, ctx, info), + teamsByOwner: (parent, arg, ctx, info) => teamsByOwner(parent, arg, ctx, info), + teamsByRole: (parent, arg, ctx, info) => teamsByRole(parent, arg, ctx, info), + owner: (parent, arg, ctx, info) => owner(parent, arg, ctx, info), + fbByTeamMember: (parent, arg, ctx, info) => fbByTeamMember(parent, arg, ctx, info) + } +} diff --git a/api/src/graphql/resolvers/index.js b/api/src/graphql/resolvers/index.js new file mode 100644 index 000000000..543ab4e32 --- /dev/null +++ b/api/src/graphql/resolvers/index.js @@ -0,0 +1,6 @@ +import merge from 'lodash.merge' + +import { resolvers as queryResolvers } from './Query' +import { resolvers as mutationResolvers } from './Mutation' + +export const resolvers = merge(queryResolvers, mutationResolvers) diff --git a/api/src/schema.graphql b/api/src/graphql/schema.graphql similarity index 72% rename from api/src/schema.graphql rename to api/src/graphql/schema.graphql index 2be3053e3..2095882bb 100644 --- a/api/src/schema.graphql +++ b/api/src/graphql/schema.graphql @@ -1,4 +1,4 @@ -# import Query.TeamConnection, Team from "./generated/prisma.graphql" +# import Query.TeamConnection, Team from "../generated/prisma.graphql" directive @isAuthenticated on QUERY | FIELD | MUTATION directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION @@ -11,12 +11,11 @@ type Query { teamByName(name: String!): Team teamById(teamId: String!): Team teamWithRole(teamId: String!, role: String!): Team - teamsByOwner(ownerId: String!): [Team!]! @isAuthenticated + teamsByOwner: [Team!]! @isAuthenticated teamsByRole: [Team!]! @isAuthenticated fbByTeamMember: Team @isAuthenticated owner: Member @isAuthenticated - currentMember: Member @isAuthenticated - member(authId: String!): Member @isAuthenticated + member: Member @isAuthenticated } type Mutation { @@ -25,7 +24,7 @@ type Mutation { createTeam(name: String!, slug: String!, botName: String!, botSlug: String!): Team @isAuthenticated updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated updateFB(fbId: String!, avatar: String): FinancialBeings @isAuthenticated - deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") + deleteTeam(slug: String!, botSlug: String!): Team @isAuthenticated @isOwner(type: "Team") sendMemberInviteSG(email: String!, teamId: String!): String confirmMember(token: String!): String removeTeamMember(teamId: String!, memberId: String!): String @@ -35,18 +34,14 @@ fragment MemberInfo on Member { id authId alias - isActive - profile { - ...MemberProfileInfo + visible + status { + ...MemberStatusInfo } } -fragment MemberProfileInfo on MemberProfile { - firstName - lastName - email - avatar - role { - name - } +fragment MemberStatusInfo on MemberStatus { + status + reason + createdAt } diff --git a/api/src/index.js b/api/src/index.js index 47bb9435e..11ed3ea81 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -13,17 +13,10 @@ import { importSchema } from 'graphql-import' import parser from 'fast-xml-parser' import axios from 'axios' -import checkJwt from './auth/middleware/jwt' -import { getMember } from './auth/middleware/getMember' -import { validateIdToken } from './auth/validateIdToken' +import { resolvers } from './graphql/resolvers' + import { schemaDirectives } from './directives' -import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from './email/sendgrid' - -import TEAMS_FRAGMENT from './graphql/TeamsFragment' -import TEAMS_CONNECTIONS_FRAGMENT from './graphql/TeamsConnectionsFragment' -import TEAM_FB_FRAGMENT from './graphql/TeamFBFragment' - import pubsub from './pubsub' import { logger } from './logger' @@ -32,286 +25,6 @@ const GRAPHQL_SUBSCRIPTIONS = '/graphql' const PORT = 4001 const NODE_ENV = 'development' -const createMember = async function (ctx, info, idToken) { - logger.info('createMember', idToken) - const member = await ctx.db.mutation.upsertMember({ - where: { - authId: idToken.sub, - }, - create: { - authId: idToken.sub, - alias: idToken.nickname - }, - update: { - authId: idToken.sub, - alias: idToken.nickname - } - }, info) - return member -} - -const ctxMember = ctx => ctx.request.user - -const resolvers = { - Query: { - async member(parent, arg, ctx, info) { - logger.info('member: ', parent, arg) - return ctx.db.query.member({ where: { authId: arg.authId } }, info) - }, - async currentMember(parent, args, ctx, info) { - logger.info('currentMember: ', ctx.request.res.req.user, ctx.token) - const authId = ctx.request.res.req.user.sub - - return ctx.db.query.member({ where: { authId } }, info) - }, - teams(parent, args, ctx, info) { - logger.info('teams: ', ctx.request.res) - return ctx.db.query.teamsConnection({}, TEAMS_CONNECTIONS_FRAGMENT) - }, - async teamById(parent, { id }, ctx, info) { - return ctx.db.query.team({ where: { id } }, info) - }, - async teamByName(parent, { name }, ctx, info) { - return ctx.db.query.team({ where: { name: name } }, `{ name }`) - }, - async teamWithRole(parent, { teamId, role }, ctx, info) { - return ctx.db.query.teamsConnection({where: {AND: [{id: teamId},{members_some:{role: role}}]}, first:1}, info) - }, - async teamsByOwner(parent, { ownerId }, ctx, info) { - return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) - }, - async teamsByRole(parent, args, ctx, info) { - const authId = ctx.request.user.sub - let teamAdmin - try { - teamAdmin= await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}, role_not:'MEMBER'}]}}}, TEAMS_FRAGMENT) - } catch (err) { - logger.debug('teamsByRole error: ') - logger.debug(err) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - return errors - } - logger.info('teamsByRole teamAdmin: ', teamAdmin) - return teamAdmin - }, - async fbByTeamMember(parent, args, ctx, info) { - logger.info('fbByTeamMember: ', args, ctx.request, ctx.request.res.req.user) - const authId = ctx.request.user.sub - let teamMemberFB = await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}}]}}, orderBy: 'updatedAt_DESC'}, TEAM_FB_FRAGMENT) - logger.info('fbByTeamMember response: ', await teamMemberFB[0]) - return teamMemberFB[0] - }, - async owner(parent, args, ctx, info) { - logger.info('resolver.query.owner ctx: ', ctxMember(ctx)) - return ctx.db.query.member({ where: { id: ctxMember(ctx).authId } }, info) - .catch(err => { - logger.debug('createTeam error: ', err) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - }) - } - }, - Mutation: { - async authenticate(parent, { idToken }, ctx, info) { - let memberToken = null - try { - memberToken = await validateIdToken(idToken) - logger.info('authenticate.memberToken: ', await memberToken) - } catch (err) { - logger.debug('authenticat.validateIdToken err: ', err) - throw new Error(err.message) - } - const authId = memberToken.sub - let member = await ctx.db.query.member({ where: { authId } }, info) - - if (!member) { - try { - return createMember(ctx, info, memberToken) - } catch (error) { - throw new Error(error.message) - } - } - return member - }, - async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { - const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) - const addMember = await ctx.db.mutation.upsertTeamMembers({ - where:{email: email}, - create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}}, - update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}}, - `{ id }`) - const sendInvite = await sendTeamMemberInvite(email, team) - return sendInvite - }, - async verifyTeamInvite(parent, { token }, ctx, info) { - let verifiedToken = null - let team = null - try { - verifiedToken = await verifyInviteToken(token) - team = { - email: verifiedToken.email, - team: { - slug: verifiedToken.team - } - } - logger.info('verifyTeamInvite.token: ', await verifiedToken) - } catch (err) { - logger.debug('verifyTeamInvite. err: ', err) - throw new Error(err.message) - } - return team - }, - async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { - logger.info('createTeam ctx.request.user:') - logger.info(ctx.request.user) - - const authId = ctx.request.user.sub - - const encodedAuthId = encodeURI(authId) - const encodedURL = `https://users-api.advancedalgos.net/graphql?query=%7B%0A%20%20userByAuthId(authId:%20%22${authId}%22)%20%7B%0A%20%20%20%20id%0A%20%20%20%20email%0A%20%20%20%20alias%0A%20%20%7D%0A%7D%0A` - const decodedURL = decodeURI(encodedURL) - return getUser(authId) - .then(async result => { - logger.info('axios:') - logger.info(result) - - const alias = result.data.users_UserByAuthId.alias - const email = result.data.users_UserByAuthId.email - const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png' - const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png' - - const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`) - logger.info('createTeamUrl:') - logger.info(JSON.stringify(await createTeamUrl)) - - const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/teamSetup/' - // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - - logger.info(`${platformUrl}${createTeamUrl}/${authId}`) - const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) - .then((result) => { - console.log('createPlatformTeam result:', result.data) - if(result.data.message === 'Team Name already taken'){ - return result.data.message - } - if(result.data.result === 'Fail'){ - return result.data.message - } - return result - }) - .catch(err =>{ - logger.debug('createPlatformTeam err:') - logger.debug(err) - throw new Error(err) - }) - console.log('createPlatformTeam returned: ', await createPlatformTeam) - if(await createPlatformTeam === 'Team Name already taken'){ - return createPlatformTeam - } - - const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) - .catch((err) => { - logger.debug(err, 'existingMember error: ') - return err - }) - - logger.info('existingMember:') - logger.info(await existingMember) - - let createTeam - if(existingMember.id !== null){ - logger.info('createTeam with existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - return err - }) - } else { - logger.info('createTeam without existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - return err - }) - } - - // sendTeamCreateConfirmation(email, name, botName) - - return createTeam - }) - .catch((err) => { - logger.debug('createTeam error: ') - return err - }) - }, - async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { - return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug('createTeam error: ', err) - return err - }) - }, - async updateFB(parent, { fbId, avatar}, ctx, info) { - return ctx.db.mutation.updateFinancialBeings({data:{avatar:avatar}, where:{id:fbId}}, info) - .catch((err) => { - logger.debug('createTeam error: ', err) - return err - }) - }, - async deleteTeam(parent, { slug }, ctx, info) { - return ctx.db.mutation.deleteTeam({ where: { slug } }, info) - .catch((res) => { - logger.info('deleteTeam error: ', res) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - }) - } - } -} - -const getUser = authId => { - logger.info('getUser') - logger.info(authId) - const API_URL = 'https://app-api.advancedalgos.net/graphql' - return new Promise(resolve => { - try { - const result = axios.post(API_URL, { - query: `query users_UserByAuthId($authId: String!) { - users_UserByAuthId(authId: $authId){ - id - alias - email - } - }`, - variables: { - authId: authId - } - }, { - headers: { - 'Content-Type': 'application/json' - } - }) - .then(result => { - logger.info('getUser result') - logger.info(result.data) - resolve(result.data) - }) - .catch(err => { - logger.info('getUser err') - logger.info(err) - throw new Error(err) - }) - - } catch (error) { - resolve(error) - } - }) -} - const db = new Prisma({ fragmentReplacements: extractFragmentReplacements(resolvers), typeDefs: 'src/generated/prisma.graphql', @@ -322,20 +35,7 @@ const db = new Prisma({ const app = express() -const connectionMiddlewares = [ - checkJwt, - function (err, req, res, next) { - logger.info('checkJwt: ', err, req.headers, req.user) - if (err) { - return res.status(201).send(err.message) - } else { - next() - } - }, - // (req, res, done) => getMember(req, res, done, db) -] - -app.post(GRAPHQL_ENDPOINT, ...connectionMiddlewares) +app.post(GRAPHQL_ENDPOINT) const corsOptions = { "origin": "*", @@ -355,16 +55,16 @@ const options = { const server = createApolloServer(app, { graphqlEndpoint: GRAPHQL_ENDPOINT, subscriptionsEndpoint: GRAPHQL_SUBSCRIPTIONS, - wsMiddlewares: connectionMiddlewares, apolloServerOptions: { formatError }, - typeDefs: importSchema('src/schema.graphql'), + typeDefs: importSchema('src/graphql/schema.graphql'), resolvers, resolverValidationOptions: { requireResolversForResolveType: false }, schemaDirectives, context: req => ({ ...req , - token: req.headers, - user: req.user, + token: req.headers ? req.headers : undefined, + user: req.user ? req.user : undefined, + userId: (req.headers && req.headers.userid) ? req.headers.userid : undefined, db, pubsub }) @@ -375,5 +75,3 @@ server.listen({ port: PORT }, () => { `🚀 GraphQL Server is running on http://localhost:${PORT}${GRAPHQL_ENDPOINT} in "${NODE_ENV}" mode\n` ) }) - -module.exports= { createMember } diff --git a/api/src/logger/errors.js b/api/src/logger/errors.js new file mode 100644 index 000000000..654dc98ef --- /dev/null +++ b/api/src/logger/errors.js @@ -0,0 +1,19 @@ +export class AuthenticationError extends Error { + code = 401 + message = 'Not Authenticated - you have to be authenticated to perform this action' +} + +export class DatabaseError extends Error { + code = 404 + message = 'Resource not found : ' + this.message +} + +export class WrongArgumentsError extends Error { + code = 400 + message = 'Wrong arguments : ' + this.message +} + +export class ServiceUnavailableError extends Error { + code = 503 + message = 'At least one service is unresponding ' + this.message +} diff --git a/api/src/logger/index.js b/api/src/logger/index.js index 3ed2a8e70..2a68e3bfc 100644 --- a/api/src/logger/index.js +++ b/api/src/logger/index.js @@ -1,6 +1,8 @@ import { createLogger, format, transports } from 'winston' import { inspect } from 'util' +import { AuthentificationError, DatabaseError, WrongArgumentsError, ServiceUnavailableError } from './errors' + class LoggerService { constructor() { const winstonTransports = [ @@ -28,4 +30,4 @@ class LoggerService { } export const logger = new LoggerService() // Default logger -export { LoggerService } +export { LoggerService, AuthentificationError, DatabaseError, WrongArgumentsError, ServiceUnavailableError } diff --git a/api/src/middleware/getMember.js b/api/src/middleware/getMember.js new file mode 100644 index 000000000..1a4498d45 --- /dev/null +++ b/api/src/middleware/getMember.js @@ -0,0 +1,57 @@ +import axios from 'axios' + +import { logger, DatabaseError } from '../logger' + +export const getMember = async (req, res, next, db) => { + if (!req.headers.userId) return next() + logger.info('getMember ') + logger.info(req.headers.userId) + try { + const member = await getUser(req.headers.userId) + req.user = await member + next() + } catch (err) { + next() + } + +} + +export const getUser = authId => { + logger.info('getUser MiddleWare') + logger.info(authId) + const API_URL = 'https://app-api.advancedalgos.net/graphql' + return new Promise((resolve, reject) => { + try { + return axios.post(API_URL, { + query: `query users_User($userId: ID!) { + users_User(id: $userId){ + id + alias + email + } + }`, + variables: { + userId: authId + } + }, { + headers: { + 'Content-Type': 'application/json' + } + }) + .then(res => { + logger.info('getUser result') + logger.info(res.data) + resolve(res.data) + }) + .catch(err => { + logger.info('getUser err') + logger.info(err) + reject(new DatabaseError(err)) + }) + } catch (error) { + reject(new DatabaseError(error)) + } + }) +} + +export default getUser diff --git a/api/src/auth/middleware/jwt.js b/api/src/middleware/jwt.js similarity index 100% rename from api/src/auth/middleware/jwt.js rename to api/src/middleware/jwt.js diff --git a/client/src/graphql/teams/DeleteTeamMutation.js b/client/src/graphql/teams/DeleteTeamMutation.js index 0db2a7ad4..ce02bc213 100644 --- a/client/src/graphql/teams/DeleteTeamMutation.js +++ b/client/src/graphql/teams/DeleteTeamMutation.js @@ -2,8 +2,8 @@ import gql from 'graphql-tag' export const DELETE_TEAM = gql` - mutation deleteTeam($slug: String!) { - teams_DeleteTeam(slug: $slug) { + mutation deleteTeam($slug: String!, $botSlug: String!) { + teams_DeleteTeam(slug: $slug, botSlug: $botSlug) { id } } diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js index adbb28c4a..730769d8b 100644 --- a/client/src/graphql/teams/GetTeamsByOwnerQuery.js +++ b/client/src/graphql/teams/GetTeamsByOwnerQuery.js @@ -2,8 +2,8 @@ import gql from 'graphql-tag' export const GET_TEAMS_BY_OWNER = gql` - query teamsByOwnerQuery($authId: String!) { - teams_TeamsByOwner(ownerId: $authId) { + query teamsByOwnerQuery { + teams_TeamsByOwner { id name slug diff --git a/client/src/utils/graphql-errors.js b/client/src/utils/graphql-errors.js index 26b9c18ce..8ada52b66 100644 --- a/client/src/utils/graphql-errors.js +++ b/client/src/utils/graphql-errors.js @@ -1,12 +1,14 @@ +import log from './log' + const ErrorResponse = { 'unique constraint': 'Sorry, already taken! Please create a unique ' } export const checkGraphQLError = error => { - console.log('checkGraphQLError ', ErrorResponse) + log.debug('checkGraphQLError ', ErrorResponse) if (error.search(/unique constraint/)) { const field = error.substring(error.indexOf('=') + 1) - console.log('unique constraint', field.replace(/ +/g, '')) + log.debug('unique constraint', field.replace(/ +/g, '')) } } diff --git a/client/src/utils/log.js b/client/src/utils/log.js new file mode 100644 index 000000000..47e819934 --- /dev/null +++ b/client/src/utils/log.js @@ -0,0 +1,7 @@ +import minilog from 'minilog' + +if (process.env.NODE_ENV === 'development') minilog.enable() + +const log = minilog('frontend') + +export default log diff --git a/client/src/views/common/components/TeamBar.js b/client/src/views/common/components/TeamBar.js index b51f22e13..324fc3d13 100644 --- a/client/src/views/common/components/TeamBar.js +++ b/client/src/views/common/components/TeamBar.js @@ -13,9 +13,12 @@ import UsersIcon from '@material-ui/icons/People' import SearchIcon from '@material-ui/icons/Search' import AdbIcon from '@material-ui/icons/Adb' import SettingsApplicationsIcon from '@material-ui/icons/SettingsApplications' +import BugReportIcon from '@material-ui/icons/BugReport' import { Link } from 'react-router-dom' +import log from '../../../utils/log' + const SettingsLink = props => const FBLink = props => const MembersLink = props => @@ -61,13 +64,13 @@ const styles = theme => ({ class TeamBar extends Component { render () { const { classes, user, match } = this.props - console.log('TeamBar: ', user, match) + log.debug('TeamBar: ', user, match) return (
- + - Manage Teams + Teams +
diff --git a/client/src/views/dashboard/components/CreateTeam.js b/client/src/views/dashboard/components/CreateTeam.js index 1dfb75850..5e655ccf1 100644 --- a/client/src/views/dashboard/components/CreateTeam.js +++ b/client/src/views/dashboard/components/CreateTeam.js @@ -10,9 +10,11 @@ import { checkGraphQLError } from '../../../utils/graphql-errors' import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' +import log from '../../../utils/log' + const CreateTeam = ({ authId }) => { let input - console.log('CreateTeam', authId) + log.debug('CreateTeam', authId) return ( { if (error) { errors = error.graphQLErrors.map(({ message }, i) => { const displayMessage = checkGraphQLError(message) - console.log('createTeam error:', displayMessage) + log.debug('createTeam error:', displayMessage) return ( {message} @@ -73,7 +75,7 @@ const handleSubmit = async (e, createTeam, input) => { const currentUser = await getItem('user') let authId = JSON.parse(currentUser) authId = authId.authId - console.log('createTeam submit: ', authId) + log.debug('createTeam submit: ', authId) const name = input.value const slug = slugify(name) await createTeam({ variables: { name, slug, owner: authId } }) diff --git a/client/src/views/dashboard/components/CreateTeamDialog.js b/client/src/views/dashboard/components/CreateTeamDialog.js index 6da04291b..6c9ce0113 100644 --- a/client/src/views/dashboard/components/CreateTeamDialog.js +++ b/client/src/views/dashboard/components/CreateTeamDialog.js @@ -18,7 +18,7 @@ import { isEmpty } from '../../../utils/js-helpers' import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' -// import { checkGraphQLError } from '../../../utils/graphql-errors' +import log from '../../../utils/log' const styles = theme => ({ dialogContainer: { @@ -207,7 +207,7 @@ export class CreateTeamDialog extends Component { async handleSubmit (e, createTeam, name, botName) { e.preventDefault() - console.log('createTeam submit: ', name, botName) + log.debug('createTeam submit: ', name, botName) const slug = this.slugify(name) const botSlug = this.slugify(botName) await createTeam({ variables: { name, slug, botName, botSlug } }) diff --git a/client/src/views/dashboard/components/CreateTeamForm.js b/client/src/views/dashboard/components/CreateTeamForm.js index 75df97b5d..ebdbaf358 100644 --- a/client/src/views/dashboard/components/CreateTeamForm.js +++ b/client/src/views/dashboard/components/CreateTeamForm.js @@ -11,6 +11,8 @@ import DialogContentText from '@material-ui/core/DialogContentText' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' +import log from '../../../utils/log' + export class CreateTeamDialog extends Component { constructor (props) { super(props) @@ -50,7 +52,7 @@ export class CreateTeamDialog extends Component { } render () { - console.log(this.props) + log.debug(this.props) return (
@@ -50,8 +52,7 @@ export const ManageTeamsItem = ({ classes, team, authId, ...props }) => { ManageTeamsItem.propTypes = { classes: PropTypes.object.isRequired, - team: PropTypes.object.isRequired, - authId: PropTypes.string.isRequired + team: PropTypes.object.isRequired } export default ManageTeamsItem diff --git a/client/src/views/manage-teams/components/ManageTeamsList.js b/client/src/views/manage-teams/components/ManageTeamsList.js index 0fdf78fae..59577baa6 100644 --- a/client/src/views/manage-teams/components/ManageTeamsList.js +++ b/client/src/views/manage-teams/components/ManageTeamsList.js @@ -1,20 +1,18 @@ import React from 'react' import PropTypes from 'prop-types' import { Query } from 'react-apollo' -import { withStateHandlers, lifecycle, compose } from 'recompose' import Grid from '@material-ui/core/Grid' import { MessageCard } from '@advancedalgos/web-components' import { withStyles } from '@material-ui/core/styles' -import { isDefined, isString } from '../../../utils/js-helpers' -import { getItem } from '../../../utils/local-storage' - import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import ManageTeamsItem from './ManageTeamsItem' import CreateTeamDialog from './CreateTeamDialog' +import log from '../../../utils/log' + const styles = theme => ({ heroContent: { maxWidth: 600, @@ -47,105 +45,66 @@ const styles = theme => ({ } }) -export const ManageTeamsList = ({ classes, user = null, ...props }) => { - let owner - let authId = null - console.log('ManageTeamsList: ', user) - if (user !== null && isString(user)) { - owner = JSON.parse(user) - if (isDefined(owner.authId)) authId = owner.authId - } - console.log('ManageTeamsList 2: ', owner, authId) - if (authId === undefined || authId === null) { - return ( - - - - - - ) - } else { - return ( - - {({ loading, error, data }) => { - console.log('GET_TEAMS_BY_OWNER: ', loading, error, data) +export const ManageTeamsList = ({ classes, user = null, ...props }) => ( + + {({ loading, error, data }) => { + log.debug('GET_TEAMS_BY_OWNER: ', loading, error, data) - let errors = null - if (error) { - errors = error.graphQLErrors.map(({ message }, i) => { - return - }) - } - if (!loading && !error) { - if (data.teams_TeamsByOwner.length > 0) { - return ( - - - {!loading && - data.teams_TeamsByOwner.map(team => ( - - ))} - {errors} - - - ) - } else { - return ( - - - - - - - - ) - } - } else { - return ( + let errors = null + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + return + }) + } + if (!loading && !error) { + if (data.teams_TeamsByOwner.length > 0) { + return ( + - - - {errors !== null && } - + {!loading && + data.teams_TeamsByOwner.map(team => ( + + ))} + {errors} - ) - } - }} - - ) - } -} + + ) + } else { + return ( + + + + + + + + ) + } + } else { + return ( + + + + {errors !== null && } + + + ) + } + }} + +) ManageTeamsList.propTypes = { user: PropTypes.any, classes: PropTypes.object.isRequired } -const getUserOnMount = lifecycle({ - componentDidMount () { - getItem('user').then(user => { - this.setState({ user }) - }) // Set user to state - } -}) - -const mapStateToProps = withStateHandlers(() => ({ user: null }), { - user: ({ user }) => () => ({ user }) -}) - -const ManageTeamsListAuthId = compose( - mapStateToProps, - getUserOnMount -)(ManageTeamsList) - -export default withStyles(styles)(ManageTeamsListAuthId) +export default withStyles(styles)(ManageTeamsList) diff --git a/client/src/views/team-members/components/ManageMemberDelete.js b/client/src/views/team-members/components/ManageMemberDelete.js index cc75e3df1..e7e731703 100644 --- a/client/src/views/team-members/components/ManageMemberDelete.js +++ b/client/src/views/team-members/components/ManageMemberDelete.js @@ -15,6 +15,7 @@ import REMOVE_TEAM_MEMBER from '../../../graphql/teams/RemoveTeamMemberMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' +import log from '../../../utils/log' const styles = theme => ({ dialogContainer: { @@ -54,7 +55,7 @@ export class ManageMemberDelete extends Component { } render () { - console.log(this.props, this.props.teamId) + log.debug(this.props, this.props.teamId) const { classes, teamId, authId } = this.props return ( { const displayMessage = checkGraphQLError(message) - console.log('createTeam error:', displayMessage) + log.debug('createTeam error:', displayMessage) return ( {message} diff --git a/client/src/views/team-members/components/ManageMemberRole.js b/client/src/views/team-members/components/ManageMemberRole.js index f428ebbb6..08823d3b7 100644 --- a/client/src/views/team-members/components/ManageMemberRole.js +++ b/client/src/views/team-members/components/ManageMemberRole.js @@ -17,6 +17,7 @@ import SET_TEAM_MEMBER_ROLE from '../../../graphql/teams/SetTeamMemberRoleMutati import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' +import log from '../../../utils/log' const styles = theme => ({ dialogContainer: { @@ -62,7 +63,7 @@ export class ManageMemberRole extends Component { } render () { - console.log(this.props, this.props.teamId) + log.debug(this.props, this.props.teamId) const { classes, teamId, authId } = this.props return ( { const displayMessage = checkGraphQLError(message) - console.log('createTeam error:', displayMessage) + log.debug('createTeam error:', displayMessage) return ( {message} diff --git a/client/src/views/team-members/components/ManageMemberStatus.js b/client/src/views/team-members/components/ManageMemberStatus.js index fba5880ee..b5267f098 100644 --- a/client/src/views/team-members/components/ManageMemberStatus.js +++ b/client/src/views/team-members/components/ManageMemberStatus.js @@ -10,6 +10,8 @@ import Card from '@material-ui/core/Card' import CardContent from '@material-ui/core/CardContent' import CardActions from '@material-ui/core/CardActions' +import log from '../../../utils/log' + const styles = theme => ({ dialogContainer: { display: 'block', @@ -47,7 +49,7 @@ export class ManageMemberStatus extends Component { } render () { - console.log(this.props, this.props.status) + log.debug(this.props, this.props.status) const { classes, status } = this.props return (
diff --git a/client/src/views/team-members/components/ManageMembersItem.js b/client/src/views/team-members/components/ManageMembersItem.js index daad67f3e..3dd2be7fb 100644 --- a/client/src/views/team-members/components/ManageMembersItem.js +++ b/client/src/views/team-members/components/ManageMembersItem.js @@ -9,8 +9,10 @@ import ManageMemberRole from './ManageMemberRole' import ManageMemberStatus from './ManageMemberStatus' import { MessageCard } from '@advancedalgos/web-components' +import log from '../../../utils/log' + export const ManageMembersItem = ({ classes, member, teamId, authId }) => { - console.log('ManageMembersItem', member) + log.debug('ManageMembersItem', member) let profile = null let email = null let memberStatus = null diff --git a/client/src/views/team-members/components/ManageMembersTeamItem.js b/client/src/views/team-members/components/ManageMembersTeamItem.js index f8bdbf102..186576377 100644 --- a/client/src/views/team-members/components/ManageMembersTeamItem.js +++ b/client/src/views/team-members/components/ManageMembersTeamItem.js @@ -12,6 +12,8 @@ import InviteMemberDialog from './InviteMemberDialog' import ManageMembersItem from './ManageMembersItem' import { MessageCard } from '@advancedalgos/web-components' +import log from '../../../utils/log' + const styles = theme => ({ heroContent: { maxWidth: 600, @@ -56,7 +58,7 @@ const styles = theme => ({ }) export const ManageMembersTeamItem = ({ classes, team, authId }) => { - console.log('ManageMembersItem', team, team.profile, team.profile.avatar) + log.debug('ManageMembersItem', team, team.profile, team.profile.avatar) let avatar if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { avatar = team.profile.avatar diff --git a/client/src/views/teams/AcceptTeamInvite.js b/client/src/views/teams/AcceptTeamInvite.js index 1dde43cf4..58fa28efa 100644 --- a/client/src/views/teams/AcceptTeamInvite.js +++ b/client/src/views/teams/AcceptTeamInvite.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { parse } from '../../utils/js-helpers' +import log from '../../utils/log' import { MessageCard } from '@advancedalgos/web-components' @@ -9,7 +10,7 @@ class AcceptTeamInvite extends Component { render () { const { location, auth } = this.props const param = parse(location.search) - console.log(param, auth) + log.debug(param, auth) if (auth === undefined) { return diff --git a/client/src/views/teams/components/TeamsList.js b/client/src/views/teams/components/TeamsList.js index a8bd4cf84..ac416122f 100644 --- a/client/src/views/teams/components/TeamsList.js +++ b/client/src/views/teams/components/TeamsList.js @@ -10,6 +10,7 @@ import { MessageCard } from '@advancedalgos/web-components' import GET_ALL_TEAMS_QUERY from '../../../graphql/teams/GetAllTeamsQuery' import { isEmpty } from '../../../utils/js-helpers' +import log from '../../../utils/log' import TeamsItem from './TeamsItem' import TeamsDetails from './TeamsDetails' @@ -42,7 +43,7 @@ const styles = theme => ({ export const TeamsList = ({ classes, match }) => ( {({ loading, error, data, ...props }) => { - console.log('GET_ALL_TEAMS: ', loading, error, data, match, props) + log.debug('GET_ALL_TEAMS: ', loading, error, data, match, props) let slug = null if (!isEmpty(match.params) && match.params.slug) slug = match.params.slug diff --git a/client/tools/log.js b/client/tools/log.js deleted file mode 100644 index 978d6d4bb..000000000 --- a/client/tools/log.js +++ /dev/null @@ -1,13 +0,0 @@ -import minilog from 'minilog' - -minilog.enable() - -const log = minilog('frontend') - -let consoleLog = global.console.log - -global.console.log = function () { - consoleLog.apply(global.console, arguments) -} - -export default log